├── .gitignore ├── GameController.iml ├── GameController.ipr ├── LICENSE ├── README.md ├── TCM.md ├── build.xml ├── deps ├── gluegen-rt-natives-linux-amd64.jar ├── gluegen-rt-natives-linux-i586.jar ├── gluegen-rt-natives-macosx-universal.jar ├── gluegen-rt-natives-windows-amd64.jar ├── gluegen-rt-natives-windows-i586.jar ├── gluegen-rt.jar ├── gluegen.LICENSE.txt ├── jogl-all-natives-linux-amd64.jar ├── jogl-all-natives-linux-i586.jar ├── jogl-all-natives-macosx-universal.jar ├── jogl-all-natives-windows-amd64.jar ├── jogl-all-natives-windows-i586.jar ├── jogl-all.jar ├── jogl.LICENSE.txt ├── snakeyaml-2.2.jar └── snakeyaml.LICENSE.txt ├── examples ├── c │ └── RoboCupGameControlData.h └── python │ ├── requirements.txt │ ├── robocup_game_control_data.py │ └── robocup_game_control_return_data.py ├── resources ├── config │ ├── icons │ │ ├── pause_icon.png │ │ ├── play_icon.png │ │ ├── plus_icon.png │ │ ├── redo.png │ │ ├── redo_disabled.png │ │ ├── reset_icon.png │ │ ├── undo.png │ │ ├── undo_disabled.png │ │ ├── wlan_status_green.png │ │ ├── wlan_status_red.png │ │ └── wlan_status_yellow.png │ └── spl │ │ ├── 1.png │ │ ├── 10.gif │ │ ├── 11.png │ │ ├── 12.gif │ │ ├── 13.png │ │ ├── 14.png │ │ ├── 15.png │ │ ├── 16.png │ │ ├── 17.png │ │ ├── 18.png │ │ ├── 19.png │ │ ├── 2.gif │ │ ├── 20.png │ │ ├── 21.png │ │ ├── 22.png │ │ ├── 23.png │ │ ├── 24.png │ │ ├── 26.gif │ │ ├── 27.png │ │ ├── 28.png │ │ ├── 29.png │ │ ├── 3.gif │ │ ├── 30.png │ │ ├── 31.png │ │ ├── 32.png │ │ ├── 33.png │ │ ├── 34.png │ │ ├── 35.png │ │ ├── 36.png │ │ ├── 37.png │ │ ├── 38.png │ │ ├── 39.png │ │ ├── 4.gif │ │ ├── 41.png │ │ ├── 44.png │ │ ├── 45.png │ │ ├── 46.png │ │ ├── 47.png │ │ ├── 48.png │ │ ├── 49.png │ │ ├── 5.png │ │ ├── 50.png │ │ ├── 51.png │ │ ├── 54.png │ │ ├── 6.gif │ │ ├── 7.png │ │ ├── 8.png │ │ ├── 9.png │ │ └── teams.cfg ├── plugins │ └── 05 │ │ ├── B-Human │ │ ├── build.xml │ │ └── src │ │ │ ├── bhuman │ │ │ ├── MANIFEST.MF │ │ │ ├── drawings │ │ │ │ ├── BallVelocity.java │ │ │ │ ├── Heat.java │ │ │ │ ├── PlayerTarget.java │ │ │ │ ├── RefereeDetected.java │ │ │ │ └── WhistleHeard.java │ │ │ ├── gui │ │ │ │ └── BHumanDetailFrame.java │ │ │ └── message │ │ │ │ ├── BHumanMessage.java │ │ │ │ └── data │ │ │ │ ├── Angle.java │ │ │ │ ├── ArrayReader.java │ │ │ │ ├── BitStream.java │ │ │ │ ├── ComplexStreamReader.java │ │ │ │ ├── Eigen.java │ │ │ │ ├── EnumMapReader.java │ │ │ │ ├── EnumReader.java │ │ │ │ ├── ListCountSize.java │ │ │ │ ├── ListReader.java │ │ │ │ ├── NativeReaders.java │ │ │ │ ├── Primitive.java │ │ │ │ ├── ProbablySimpleStreamReader.java │ │ │ │ ├── Reader.java │ │ │ │ ├── SimpleStreamReader.java │ │ │ │ ├── StreamReader.java │ │ │ │ ├── StreamedObject.java │ │ │ │ └── Timestamp.java │ │ │ └── util │ │ │ └── Unsigned.java │ │ └── resources │ │ ├── fire!_icon.png │ │ ├── fire_icon.png │ │ ├── heat_icon.png │ │ ├── kickInLeft.png │ │ ├── kickInRight.png │ │ ├── ready.png │ │ └── whistle.png └── scene │ ├── Ball2010SPL.rsi2 │ ├── FieldsSPL.rsi2 │ ├── NaoV4H21.rsi2 │ ├── TeamComm.ros2 │ └── Textures │ ├── ball_icon.png │ ├── net.png │ ├── shadow.png │ ├── textures-button_gray.png │ └── textures-ear.png └── src ├── common ├── ApplicationLock.java ├── Log.java └── net │ ├── GameControlReturnDataPackage.java │ ├── GameControlReturnDataReceiver.java │ ├── SPLTeamMessagePackage.java │ ├── SPLTeamMessageReceiver.java │ └── logging │ └── Logger.java ├── data ├── GameControlData.java ├── GameControlReturnData.java ├── PlayerInfo.java ├── Rules.java ├── SPL.java ├── SPLTeamMessage.java ├── TeamInfo.java ├── Teams.java └── TrueDataRequest.java ├── eventrecorder ├── EventRecorder.java ├── MANIFEST.MF ├── action │ ├── Action.java │ ├── ActionHistory.java │ ├── CircularLiFoBuffer.java │ ├── EntryChangeTextAction.java │ ├── EntryChangeTimeAction.java │ ├── EntryCreateAction.java │ ├── EntryDeleteAction.java │ ├── EntryTypeChangeAction.java │ └── TitleChangeAction.java ├── data │ ├── DataModel.java │ ├── LogEntry.java │ └── LogType.java ├── export │ └── MarkDownExporter.java └── gui │ ├── EntryPanel.java │ ├── ImageButton.java │ ├── ImageToggleButton.java │ ├── LogEntryTable.java │ ├── MainFrame.java │ ├── MenuBar.java │ ├── TextField.java │ └── TimeField.java ├── exporter ├── LogExporter.java └── MANIFEST.MF ├── teamcomm ├── Config.java ├── MANIFEST.MF ├── PluginLoader.java ├── TeamCommunicationMonitor.java ├── data │ ├── AdvancedMessage.java │ ├── GameState.java │ ├── RobotState.java │ └── event │ │ ├── GameControlDataEvent.java │ │ ├── GameControlDataEventListener.java │ │ ├── GameControlDataTimeoutEvent.java │ │ ├── RobotStateEvent.java │ │ ├── RobotStateEventListener.java │ │ ├── TeamEvent.java │ │ └── TeamEventListener.java ├── gui │ ├── Camera.java │ ├── LogReplayFrame.java │ ├── MainWindow.java │ ├── RobotDetailFrame.java │ ├── RobotDetailFrameDefault.java │ ├── RobotPanel.java │ ├── TeamLogoLoader.java │ ├── View3D.java │ ├── View3DCanvas.java │ ├── View3DGSV.java │ └── drawings │ │ ├── Drawing.java │ │ ├── Image.java │ │ ├── PerPlayer.java │ │ ├── PerPlayerWithTeam.java │ │ ├── RoSi2Element.java │ │ ├── RoSi2Loader.java │ │ ├── Static.java │ │ ├── Text.java │ │ ├── TextureLoader.java │ │ └── common │ │ ├── Ball.java │ │ ├── Field.java │ │ ├── Field2015.java │ │ ├── FieldSmall.java │ │ ├── GameControllerInfo.java │ │ ├── MANIFEST.MF │ │ ├── Player.java │ │ └── PlayerNumber.java └── net │ ├── GameControlDataReceiver.java │ ├── GameControlReturnDataReceiverTCM.java │ ├── SPLTeamMessageReceiverTCM.java │ └── logging │ ├── LogReplayEvent.java │ ├── LogReplayEventListener.java │ ├── LogReplayTask.java │ ├── LogReplayer.java │ └── LogYamlLoader.java └── tester ├── GameControllerTester.java ├── MANIFEST.MF └── MainWindow.java /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | build/ 3 | nbproject/ 4 | .settings/ 5 | .idea 6 | *~ 7 | error.txt 8 | /resources/plugins/05/bhuman.jar 9 | GameController.iws 10 | -------------------------------------------------------------------------------- /GameController.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 The RoboCup SPL GameController Maintainers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | This license does not apply to the subdirectories `deps` and `resources`. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RoboCup SPL GameController Support Tools 2 | 3 | These are the GameController support tools developed by team B-Human for 4 | the RoboCup SPL. Originally, this repository contained the GameController 5 | as well. It was used in the RoboCup SPL from 2013 to 2022. Since 2023, 6 | [a new version](https://github.com/RoboCup-SPL/GameController3) is being used. 7 | 8 | Please note that the Humanoid League currently uses a 9 | [fork](https://github.com/RoboCup-Humanoid-TC/GameController). 10 | 11 | The sources mentioned in some sections of this document are available at 12 | [https://github.com/RoboCup-SPL/GameController](https://github.com/RoboCup-SPL/GameController). 13 | 14 | 15 | ### Acknowledgement 16 | 17 | The development was partially supported by the RoboCup Federation within the 18 | calls for Support for Projects for League Developments for 2013, 2015, 2017, 19 | and 2018. 20 | 21 | 22 | ## 1. Building from Source 23 | 24 | To build it from the source code you may use Apache Ant. 25 | Just call "ant" in the main directory. 26 | 27 | Building the source code requires the JDK 1.8 or newer. 28 | 29 | 30 | ## 2. GameStateVisualizer 31 | 32 | As of the 2017 RoboCup competitions, the GameStateVisualizer has been replaced 33 | by the GameStateVisualizer mode of the TeamCommunicationMonitor. 34 | 35 | To start it, run 36 | 37 | `java -jar TeamCommunicationMonitor.jar --gsv -l `. 38 | 39 | The parameter `-l` (or `--league`) is optional; if it is not given, the GSV 40 | assumes a game in the SPL. 41 | 42 | 43 | ## 3. TeamCommunicationMonitor 44 | 45 | The TeamCommunicationMonitor (TCM) is a tool for visualizing the data 46 | communicated by robots during SPL games. 47 | 48 | It serves two main purposes: 49 | 50 | 1. offering diagnostic data such as which robots are communicating on which 51 | team ports. 52 | 2. visualizing the data that was sent via SPLStandardMessages in both textual 53 | and graphical form. 54 | 55 | For more info see [TCM](TCM.md). 56 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /deps/gluegen-rt-natives-linux-amd64.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/gluegen-rt-natives-linux-amd64.jar -------------------------------------------------------------------------------- /deps/gluegen-rt-natives-linux-i586.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/gluegen-rt-natives-linux-i586.jar -------------------------------------------------------------------------------- /deps/gluegen-rt-natives-macosx-universal.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/gluegen-rt-natives-macosx-universal.jar -------------------------------------------------------------------------------- /deps/gluegen-rt-natives-windows-amd64.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/gluegen-rt-natives-windows-amd64.jar -------------------------------------------------------------------------------- /deps/gluegen-rt-natives-windows-i586.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/gluegen-rt-natives-windows-i586.jar -------------------------------------------------------------------------------- /deps/gluegen-rt.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/gluegen-rt.jar -------------------------------------------------------------------------------- /deps/jogl-all-natives-linux-amd64.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/jogl-all-natives-linux-amd64.jar -------------------------------------------------------------------------------- /deps/jogl-all-natives-linux-i586.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/jogl-all-natives-linux-i586.jar -------------------------------------------------------------------------------- /deps/jogl-all-natives-macosx-universal.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/jogl-all-natives-macosx-universal.jar -------------------------------------------------------------------------------- /deps/jogl-all-natives-windows-amd64.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/jogl-all-natives-windows-amd64.jar -------------------------------------------------------------------------------- /deps/jogl-all-natives-windows-i586.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/jogl-all-natives-windows-i586.jar -------------------------------------------------------------------------------- /deps/jogl-all.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/jogl-all.jar -------------------------------------------------------------------------------- /deps/snakeyaml-2.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/deps/snakeyaml-2.2.jar -------------------------------------------------------------------------------- /examples/python/requirements.txt: -------------------------------------------------------------------------------- 1 | construct -------------------------------------------------------------------------------- /examples/python/robocup_game_control_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | Library to convert binary udp data to a meaningful python object. 3 | 4 | Example of listening to the GameController, and accessing the data using this library:: 5 | 6 | from robocup_game_control_data import GAMECONTROLLER_DATA_PORT, RoboCupGameControlData 7 | import socket 8 | 9 | # Setup UDP client 10 | client = socket.socket( 11 | socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # UDP 12 | client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # SO_REUSEADDR instead of SO_REUSEPORT to work while TCM is running 13 | client.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) 14 | client.bind(('', GAMECONTROLLER_DATA_PORT)) 15 | 16 | # Receive data 17 | data, _ = client.recvfrom(1024) 18 | 19 | # Parse it 20 | parsed = RoboCupGameControlData.parse(data) 21 | 22 | # Accessing data 23 | print('First team player4 penalty: ', 24 | parsed.teams[0].players[3].penalty) 25 | print('state: ', parsed.state) 26 | """ 27 | 28 | 29 | from construct import Array, Byte, Const, Int16sl, Int16ul, Struct 30 | 31 | GAMECONTROLLER_DATA_PORT = 3838 32 | GAMECONTROLLER_RETURN_PORT = 3939 33 | 34 | GAMECONTROLLER_STRUCT_HEADER = b'RGme' 35 | GAMECONTROLLER_STRUCT_VERSION = 18 36 | 37 | MAX_NUM_PLAYERS = 20 38 | 39 | # SPL 40 | TEAM_BLUE = 0 # blue, cyan 41 | TEAM_RED = 1 # red, magenta, pink 42 | TEAM_YELLOW = 2 # yellow 43 | TEAM_BLACK = 3 # black, dark gray 44 | TEAM_WHITE = 4 # white 45 | TEAM_GREEN = 5 # green 46 | TEAM_ORANGE = 6 # orange 47 | TEAM_PURPLE = 7 # purple, violet 48 | TEAM_BROWN = 8 # brown 49 | TEAM_GRAY = 9 # lighter gray 50 | 51 | COMPETITION_PHASE_ROUNDROBIN = 0 52 | COMPETITION_PHASE_PLAYOFF = 1 53 | 54 | COMPETITION_TYPE_NORMAL = 0 55 | COMPETITION_TYPE_SHARED_AUTONOMY = 1 56 | 57 | GAME_PHASE_NORMAL = 0 58 | GAME_PHASE_PENALTYSHOOT = 1 59 | GAME_PHASE_OVERTIME = 2 60 | GAME_PHASE_TIMEOUT = 3 61 | 62 | STATE_INITIAL = 0 63 | STATE_READY = 1 64 | STATE_SET = 2 65 | STATE_PLAYING = 3 66 | STATE_FINISHED = 4 67 | STATE_STANDBY = 5 68 | 69 | SET_PLAY_NONE = 0 70 | SET_PLAY_GOAL_KICK = 1 71 | SET_PLAY_PUSHING_FREE_KICK = 2 72 | SET_PLAY_CORNER_KICK = 3 73 | SET_PLAY_KICK_IN = 4 74 | SET_PLAY_PENALTY_KICK = 5 75 | 76 | PENALTY_NONE = 0 77 | # SPL 78 | PENALTY_SPL_ILLEGAL_BALL_CONTACT = 1 # ball holding / playing with hands 79 | PENALTY_SPL_PLAYER_PUSHING = 2 80 | PENALTY_SPL_ILLEGAL_MOTION_IN_SET = 3 # heard whistle too early? 81 | PENALTY_SPL_INACTIVE_PLAYER = 4 # fallen, inactive 82 | PENALTY_SPL_ILLEGAL_POSITION = 5 83 | PENALTY_SPL_LEAVING_THE_FIELD = 6 84 | PENALTY_SPL_REQUEST_FOR_PICKUP = 7 85 | PENALTY_SPL_LOCAL_GAME_STUCK = 8 86 | PENALTY_SPL_ILLEGAL_POSITION_IN_SET = 9 87 | PENALTY_SPL_PLAYER_STANCE = 10 88 | PENALTY_SPL_ILLEGAL_MOTION_IN_STANDBY = 11 89 | 90 | PENALTY_SUBSTITUTE = 14 91 | PENALTY_MANUAL = 15 92 | 93 | RobotInfo = Struct( 94 | 'penalty' / Byte, # penalty state of the player 95 | 'secsTillUnpenalised' / Byte # estimate of time till unpenalised 96 | ) 97 | 98 | TeamInfo = Struct( 99 | 'teamNumber' / Byte, # unique team number 100 | 'fieldPlayerColour' / Byte, # colour of the field players 101 | 'goalkeeperColour' / Byte, # colour of the goalkeeper 102 | 'goalkeeper' / Byte, # player number of the goalkeeper (1-MAX_NUM_PLAYERS) 103 | 'score' / Byte, # team's score 104 | 'penaltyShot' / Byte, # penalty shot counter 105 | 'singleShots' / Int16ul, # bits represent penalty shot success # noqa: E501 106 | 'messageBudget' / Int16ul, # number of team messages the team is allowed to send for the remainder of the game # noqa: E501 107 | 'players' / Array(MAX_NUM_PLAYERS, RobotInfo) # the team's players 108 | ) 109 | 110 | RoboCupGameControlData = Struct( 111 | 'header' / Const(GAMECONTROLLER_STRUCT_HEADER), # header to identify the structure # noqa: E501 112 | 'version' / Const(GAMECONTROLLER_STRUCT_VERSION, Byte), # version of the data structure # noqa: E501 113 | 'packetNumber' / Byte, # number incremented with each packet sent (with wraparound) # noqa: E501 114 | 'playersPerTeam' / Byte, # the number of players on a team 115 | 'competitionPhase' / Byte, # phase of the competition (COMPETITION_PHASE_ROUNDROBIN, COMPETITION_PHASE_PLAYOFF) # noqa: E501 116 | 'competitionType' / Byte, # type of the competition (COMPETITION_TYPE_NORMAL, COMPETITION_TYPE_SHARED_AUTONOMY) # noqa: E501 117 | 'gamePhase' / Byte, # phase of the game (GAME_PHASE_NORMAL, GAME_PHASE_PENALTYSHOOT, etc) # noqa: E501 118 | 'state' / Byte, # state of the game (STATE_READY, STATE_PLAYING, etc) # noqa: E501 119 | 'setPlay' / Byte, # active set play (SET_PLAY_NONE, SET_PLAY_GOAL_KICK, etc) # noqa: E501 120 | 'firstHalf' / Byte, # 1 = game in first half, 0 otherwise 121 | 'kickingTeam' / Byte, # the team number of the next team to kick off, free kick etc 122 | 'secsRemaining' / Int16sl, # estimate of number of seconds remaining in the half 123 | 'secondaryTime' / Int16sl, # number of seconds shown as secondary time (remaining ready, until free ball, etc) # noqa: E501 124 | 'teams' / Array(2, TeamInfo) 125 | ) 126 | -------------------------------------------------------------------------------- /examples/python/robocup_game_control_return_data.py: -------------------------------------------------------------------------------- 1 | """ 2 | Library to perform conversion between binary udp data and a meaningful python object. 3 | 4 | Example of sending return data to the GameController:: 5 | 6 | import socket 7 | from construct import Container 8 | from robocup_game_control_data import GAMECONTROLLER_DATA_PORT 9 | from robocup_game_control_return_data import GAMECONTROLLER_RETURN_PORT, RoboCupGameControlReturnData 10 | 11 | # Setup UDP client 12 | with socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) as client: 13 | # Listen to single GC packet to determine its address 14 | client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 15 | client.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) 16 | client.bind(('', GAMECONTROLLER_DATA_PORT)) 17 | _, (address, _) = client.recvfrom(1024) 18 | 19 | # Create sample binary data packet 20 | container = Container( 21 | teamNum=5, 22 | playerNum=5, 23 | ) 24 | data = RoboCupGameControlReturnData.build(container) 25 | 26 | # Return data directly to the GameController's address and return port 27 | client.sendto(data, (address, GAMECONTROLLER_RETURN_PORT)) 28 | """ 29 | 30 | from construct import Array, Byte, Const, Default, Float32l, Struct 31 | 32 | GAMECONTROLLER_RETURN_PORT = 3939 33 | 34 | GAMECONTROLLER_RETURN_STRUCT_HEADER = b'RGrt' 35 | GAMECONTROLLER_RETURN_STRUCT_VERSION = 4 36 | 37 | RoboCupGameControlReturnData = Struct( 38 | 'header' / Const(GAMECONTROLLER_RETURN_STRUCT_HEADER), # "RGrt" 39 | 'version' / Const(GAMECONTROLLER_RETURN_STRUCT_VERSION, Byte), # has to be set to GAMECONTROLLER_RETURN_STRUCT_VERSION 40 | 'playerNum' / Default(Byte, 0), # player number starts with 1 41 | 'teamNum' / Default(Byte, 0), # team number 42 | 'fallen' / Default(Byte, 255), # 1 means that the robot is fallen, 0 means that the robot can play 43 | # position and orientation of robot 44 | # coordinates in millimeters 45 | # 0,0 is in center of field 46 | # +ve x-axis points towards the goal we are attempting to score on 47 | # +ve y-axis is 90 degrees counter clockwise from the +ve x-axis 48 | # angle in radians, 0 along the +x axis, increasing counter clockwise 49 | 'pose' / Default(Array(3, Float32l), [0, 0, 0]), # x,y,theta 50 | # ball information 51 | 'ballAge' / Default(Float32l, -1), # seconds since this robot last saw the ball. -1.f if we haven't seen it 52 | # position of ball relative to the robot 53 | # coordinates in millimeters 54 | # 0,0 is in center of the robot 55 | # +ve x-axis points forward from the robot 56 | # +ve y-axis is 90 degrees counter clockwise from the +ve x-axis 57 | 'ball' / Default(Array(2, Float32l), [0, 0]) 58 | ) 59 | -------------------------------------------------------------------------------- /resources/config/icons/pause_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/icons/pause_icon.png -------------------------------------------------------------------------------- /resources/config/icons/play_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/icons/play_icon.png -------------------------------------------------------------------------------- /resources/config/icons/plus_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/icons/plus_icon.png -------------------------------------------------------------------------------- /resources/config/icons/redo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/icons/redo.png -------------------------------------------------------------------------------- /resources/config/icons/redo_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/icons/redo_disabled.png -------------------------------------------------------------------------------- /resources/config/icons/reset_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/icons/reset_icon.png -------------------------------------------------------------------------------- /resources/config/icons/undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/icons/undo.png -------------------------------------------------------------------------------- /resources/config/icons/undo_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/icons/undo_disabled.png -------------------------------------------------------------------------------- /resources/config/icons/wlan_status_green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/icons/wlan_status_green.png -------------------------------------------------------------------------------- /resources/config/icons/wlan_status_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/icons/wlan_status_red.png -------------------------------------------------------------------------------- /resources/config/icons/wlan_status_yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/icons/wlan_status_yellow.png -------------------------------------------------------------------------------- /resources/config/spl/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/1.png -------------------------------------------------------------------------------- /resources/config/spl/10.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/10.gif -------------------------------------------------------------------------------- /resources/config/spl/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/11.png -------------------------------------------------------------------------------- /resources/config/spl/12.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/12.gif -------------------------------------------------------------------------------- /resources/config/spl/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/13.png -------------------------------------------------------------------------------- /resources/config/spl/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/14.png -------------------------------------------------------------------------------- /resources/config/spl/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/15.png -------------------------------------------------------------------------------- /resources/config/spl/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/16.png -------------------------------------------------------------------------------- /resources/config/spl/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/17.png -------------------------------------------------------------------------------- /resources/config/spl/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/18.png -------------------------------------------------------------------------------- /resources/config/spl/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/19.png -------------------------------------------------------------------------------- /resources/config/spl/2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/2.gif -------------------------------------------------------------------------------- /resources/config/spl/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/20.png -------------------------------------------------------------------------------- /resources/config/spl/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/21.png -------------------------------------------------------------------------------- /resources/config/spl/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/22.png -------------------------------------------------------------------------------- /resources/config/spl/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/23.png -------------------------------------------------------------------------------- /resources/config/spl/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/24.png -------------------------------------------------------------------------------- /resources/config/spl/26.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/26.gif -------------------------------------------------------------------------------- /resources/config/spl/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/27.png -------------------------------------------------------------------------------- /resources/config/spl/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/28.png -------------------------------------------------------------------------------- /resources/config/spl/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/29.png -------------------------------------------------------------------------------- /resources/config/spl/3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/3.gif -------------------------------------------------------------------------------- /resources/config/spl/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/30.png -------------------------------------------------------------------------------- /resources/config/spl/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/31.png -------------------------------------------------------------------------------- /resources/config/spl/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/32.png -------------------------------------------------------------------------------- /resources/config/spl/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/33.png -------------------------------------------------------------------------------- /resources/config/spl/34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/34.png -------------------------------------------------------------------------------- /resources/config/spl/35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/35.png -------------------------------------------------------------------------------- /resources/config/spl/36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/36.png -------------------------------------------------------------------------------- /resources/config/spl/37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/37.png -------------------------------------------------------------------------------- /resources/config/spl/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/38.png -------------------------------------------------------------------------------- /resources/config/spl/39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/39.png -------------------------------------------------------------------------------- /resources/config/spl/4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/4.gif -------------------------------------------------------------------------------- /resources/config/spl/41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/41.png -------------------------------------------------------------------------------- /resources/config/spl/44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/44.png -------------------------------------------------------------------------------- /resources/config/spl/45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/45.png -------------------------------------------------------------------------------- /resources/config/spl/46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/46.png -------------------------------------------------------------------------------- /resources/config/spl/47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/47.png -------------------------------------------------------------------------------- /resources/config/spl/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/48.png -------------------------------------------------------------------------------- /resources/config/spl/49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/49.png -------------------------------------------------------------------------------- /resources/config/spl/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/5.png -------------------------------------------------------------------------------- /resources/config/spl/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/50.png -------------------------------------------------------------------------------- /resources/config/spl/51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/51.png -------------------------------------------------------------------------------- /resources/config/spl/54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/54.png -------------------------------------------------------------------------------- /resources/config/spl/6.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/6.gif -------------------------------------------------------------------------------- /resources/config/spl/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/7.png -------------------------------------------------------------------------------- /resources/config/spl/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/8.png -------------------------------------------------------------------------------- /resources/config/spl/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/config/spl/9.png -------------------------------------------------------------------------------- /resources/config/spl/teams.cfg: -------------------------------------------------------------------------------- 1 | 0=Invisibles 2 | 1=UT Austin Villa 3 | 2=Austrian Kangaroos 4 | 3=Bembelbots 5 | 4=Berlin United 6 | 5=B-Human 7 | 6=Cerberus 8 | 7=DAInamite 9 | 8=Dutch Nao Team 10 | 9=Edinferno 11 | 10=Kouretes 12 | 11=MiPal 13 | 12=Nao Devils 14 | 13=HTWK Robots 15 | 14=Northern Bites 16 | 15=NTU RoboPAL 17 | 16=RoboCanes 18 | 17=RoboEireann 19 | 18=rUNSWift 20 | 19=SPQR Team 21 | 20=TJArk 22 | 21=UChile Robotics Team 23 | 22=UPennalizers 24 | 23=Crude Scientists 25 | 24=HULKs 26 | 26=MRL-SPL 27 | 27=Philosopher 28 | 28=Rimal Team 29 | 29=SpelBots 30 | 30=Team-NUST 31 | 31=UnBeatables 32 | 32=UTH-CAR 33 | 33=NomadZ 34 | 34=SPURT 35 | 35=Blue Spider 36 | 36=Camellia Dragons 37 | 37=JoiTech-SPL 38 | 38=Linköping Humanoids 39 | 39=WrightOcean 40 | 40=Mars 41 | 41=Aztlan 42 | 42=CMSingle 43 | 43=TeamSP 44 | 44=Luxembourg United 45 | 45=Naova 46 | 46=Recife Soccer 47 | 47=Rinobot-Jaguar 48 | 48=Starkit 49 | 49=SABANA Herons 50 | 50=R-ZWEI KICKERS 51 | 51=RedBackBots 52 | 52=BadgerBots 53 | 53=RoboIME 54 | 54=WisTex United 55 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Author: Felix Thielke 3 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/drawings/BallVelocity.java: -------------------------------------------------------------------------------- 1 | package bhuman.drawings; 2 | 3 | import bhuman.message.BHumanMessage; 4 | import com.jogamp.opengl.GL2; 5 | import data.GameControlReturnData; 6 | import data.PlayerInfo; 7 | import teamcomm.data.RobotState; 8 | import teamcomm.gui.Camera; 9 | import teamcomm.gui.drawings.PerPlayer; 10 | 11 | /** 12 | * Custom drawing for the ball velocity. 13 | * 14 | * @author Arne Hasselbring, merged from the former Ball drawing and Obstacle drawing 15 | */ 16 | public class BallVelocity extends PerPlayer { 17 | 18 | private static final float BALL_RADIUS = 0.0325f; 19 | private static final float MAX_BALLAGE = 5.f; 20 | 21 | @Override 22 | public void draw(final GL2 gl, final RobotState rs, final Camera camera) { 23 | if (rs.getLastTeamMessage() != null 24 | && rs.getLastTeamMessage().valid 25 | && rs.getLastTeamMessage() instanceof BHumanMessage 26 | && rs.getLastGCRDMessage() != null 27 | && rs.getLastGCRDMessage().valid) { 28 | final BHumanMessage bhMsg = (BHumanMessage) rs.getLastTeamMessage(); 29 | final GameControlReturnData gcMsg = rs.getLastGCRDMessage(); 30 | if (gcMsg.ballAge != -1.f && gcMsg.ballAge < MAX_BALLAGE && rs.getPenalty() == PlayerInfo.PENALTY_NONE) { 31 | gl.glPushMatrix(); 32 | 33 | gl.glTranslatef(gcMsg.pose[0] / 1000.0f, gcMsg.pose[1] / 1000.f, 0); 34 | gl.glRotatef((float) Math.toDegrees(gcMsg.pose[2]), 0, 0, 1); 35 | gl.glTranslatef(gcMsg.ball[0] / 1000.0f, gcMsg.ball[1] / 1000.f, 0); 36 | gl.glRotatef((float) Math.toDegrees(bhMsg.theRobotPose.rotation.radians - gcMsg.pose[2]), 0, 0, 1); 37 | 38 | gl.glBegin(GL2.GL_LINES); 39 | gl.glColor3f(1, 0, 0); 40 | gl.glNormal3f(0, 0, 1); 41 | gl.glVertex3f(0, 0, BALL_RADIUS); 42 | gl.glVertex3f(bhMsg.theBallModel.estimate.velocity.x / 1000.f, bhMsg.theBallModel.estimate.velocity.y / 1000.f, BALL_RADIUS); 43 | gl.glEnd(); 44 | 45 | gl.glPopMatrix(); 46 | } 47 | } 48 | } 49 | 50 | @Override 51 | public boolean hasAlpha() { 52 | return false; 53 | } 54 | 55 | @Override 56 | public int getPriority() { 57 | return 500; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/drawings/Heat.java: -------------------------------------------------------------------------------- 1 | package bhuman.drawings; 2 | 3 | import bhuman.message.BHumanMessage; 4 | import com.jogamp.opengl.GL2; 5 | import data.GameControlReturnData; 6 | import data.PlayerInfo; 7 | import data.Rules; 8 | import data.SPL; 9 | import java.io.File; 10 | import java.io.IOException; 11 | import javax.swing.JOptionPane; 12 | import teamcomm.data.RobotState; 13 | import teamcomm.gui.Camera; 14 | import teamcomm.gui.drawings.Image; 15 | import teamcomm.gui.drawings.PerPlayer; 16 | import teamcomm.gui.drawings.TextureLoader; 17 | 18 | /** 19 | * Custom drawing for visualizing when a robot's joints are hot. 20 | * 21 | * @author Felix Thielke 22 | */ 23 | public class Heat extends PerPlayer { 24 | 25 | private static final String[] filenames = { 26 | null, 27 | "heat_icon.png", 28 | "fire_icon.png", 29 | "fire!_icon.png" 30 | }; 31 | 32 | @Override 33 | public void draw(final GL2 gl, final RobotState rs, final Camera camera) { 34 | if (rs.getLastTeamMessage() != null 35 | && rs.getLastTeamMessage().valid 36 | && rs.getLastTeamMessage() instanceof BHumanMessage 37 | && rs.getLastGCRDMessage() != null 38 | && rs.getLastGCRDMessage().valid) { 39 | final BHumanMessage bhMsg = (BHumanMessage) rs.getLastTeamMessage(); 40 | final GameControlReturnData gcMsg = rs.getLastGCRDMessage(); 41 | final String image; 42 | if ((image = filenames[bhMsg.maxJointTemperatureStatus]) != null) { 43 | gl.glPushMatrix(); 44 | 45 | if (rs.getPenalty() != PlayerInfo.PENALTY_NONE && !(Rules.league instanceof SPL 46 | && (rs.getPenalty() == PlayerInfo.PENALTY_SPL_ILLEGAL_MOTION_IN_STANDBY 47 | || rs.getPenalty() == PlayerInfo.PENALTY_SPL_ILLEGAL_MOTION_IN_SET))) { 48 | gl.glTranslatef(-bhMsg.playerNumber, -3.5f, 1.f); 49 | } else { 50 | gl.glTranslatef(gcMsg.pose[0] / 1000.f, gcMsg.pose[1] / 1000.f, 1.f); 51 | } 52 | 53 | camera.turnTowardsCamera(gl); 54 | try { 55 | final File f = new File("plugins/" + (rs.getTeamNumber() < 10 ? "0" + rs.getTeamNumber() : String.valueOf(rs.getTeamNumber())) + "/resources/" + image).getAbsoluteFile(); 56 | Image.drawImage(gl, TextureLoader.getInstance().loadTexture(gl, f), 0, 0, 0.2f); 57 | } catch (IOException ex) { 58 | JOptionPane.showMessageDialog(null, 59 | "Error loading texture: " + ex.getMessage(), 60 | ex.getClass().getSimpleName(), 61 | JOptionPane.ERROR_MESSAGE); 62 | } 63 | gl.glPopMatrix(); 64 | } 65 | } 66 | } 67 | 68 | @Override 69 | public boolean hasAlpha() { 70 | return true; 71 | } 72 | 73 | @Override 74 | public int getPriority() { 75 | return 9; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/drawings/RefereeDetected.java: -------------------------------------------------------------------------------- 1 | package bhuman.drawings; 2 | 3 | import bhuman.message.BHumanMessage; 4 | import com.jogamp.opengl.GL2; 5 | import data.GameControlReturnData; 6 | import data.PlayerInfo; 7 | import data.Rules; 8 | import data.SPL; 9 | import java.io.File; 10 | import java.io.IOException; 11 | import javax.swing.JOptionPane; 12 | import teamcomm.data.RobotState; 13 | import teamcomm.gui.Camera; 14 | import teamcomm.gui.drawings.Image; 15 | import teamcomm.gui.drawings.PerPlayer; 16 | import teamcomm.gui.drawings.TextureLoader; 17 | 18 | /** 19 | * Custom drawing for visualizing when a referee signal was detected. 20 | * 21 | * @author Felix Thielke 22 | * @author Thomas Röfer 23 | */ 24 | public class RefereeDetected extends PerPlayer { 25 | 26 | @Override 27 | public void draw(final GL2 gl, final RobotState rs, final Camera camera) { 28 | if (rs.getLastTeamMessage() != null 29 | && rs.getLastTeamMessage().valid 30 | && rs.getLastTeamMessage() instanceof BHumanMessage 31 | && rs.getLastGCRDMessage() != null 32 | && rs.getLastGCRDMessage().valid) { 33 | final BHumanMessage bhMsg = (BHumanMessage) rs.getLastTeamMessage(); 34 | final GameControlReturnData gcMsg = rs.getLastGCRDMessage(); 35 | if (bhMsg.theRefereeSignal.signal == BHumanMessage.RefereeGesture__Gesture.ready 36 | || bhMsg.theRefereeSignal.signal == BHumanMessage.RefereeGesture__Gesture.kickInLeft 37 | || bhMsg.theRefereeSignal.signal == BHumanMessage.RefereeGesture__Gesture.kickInRight) { 38 | final long timeWhenDetected = rs.getLastTeamMessageTimestamp() - 39 | bhMsg.theFrameInfo.time.getTimeSince(bhMsg.theRefereeSignal.timeWhenDetected.timestamp); 40 | if (Math.abs(System.currentTimeMillis() - timeWhenDetected) <= 2000) { 41 | gl.glPushMatrix(); 42 | 43 | if (rs.getPenalty() != PlayerInfo.PENALTY_NONE && !(Rules.league instanceof SPL 44 | && (rs.getPenalty() == PlayerInfo.PENALTY_SPL_ILLEGAL_MOTION_IN_STANDBY 45 | || rs.getPenalty() == PlayerInfo.PENALTY_SPL_ILLEGAL_MOTION_IN_SET))) { 46 | gl.glTranslatef(-bhMsg.playerNumber, -3.5f, 1.f); 47 | } else { 48 | gl.glTranslatef(gcMsg.pose[0] / 1000.f, gcMsg.pose[1] / 1000.f, 1.f); 49 | } 50 | 51 | camera.turnTowardsCamera(gl); 52 | try { 53 | final File f = new File("plugins/" + (rs.getTeamNumber() < 10 ? "0" + rs.getTeamNumber() : String.valueOf(rs.getTeamNumber())) 54 | + "/resources/" + bhMsg.theRefereeSignal.signal + ".png").getAbsoluteFile(); 55 | Image.drawImage(gl, TextureLoader.getInstance().loadTexture(gl, f), 0, 0, 0.3f); 56 | } catch (IOException ex) { 57 | JOptionPane.showMessageDialog(null, 58 | "Error loading texture: " + ex.getMessage(), 59 | ex.getClass().getSimpleName(), 60 | JOptionPane.ERROR_MESSAGE); 61 | } 62 | gl.glPopMatrix(); 63 | } 64 | } 65 | } 66 | } 67 | 68 | @Override 69 | public boolean hasAlpha() { 70 | return true; 71 | } 72 | 73 | @Override 74 | public int getPriority() { 75 | return 10; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/drawings/WhistleHeard.java: -------------------------------------------------------------------------------- 1 | package bhuman.drawings; 2 | 3 | import bhuman.message.BHumanMessage; 4 | import com.jogamp.opengl.GL2; 5 | import data.GameControlReturnData; 6 | import data.PlayerInfo; 7 | import data.Rules; 8 | import data.SPL; 9 | import java.io.File; 10 | import java.io.IOException; 11 | import javax.swing.JOptionPane; 12 | import teamcomm.data.RobotState; 13 | import teamcomm.gui.Camera; 14 | import teamcomm.gui.drawings.Image; 15 | import teamcomm.gui.drawings.PerPlayer; 16 | import teamcomm.gui.drawings.TextureLoader; 17 | 18 | /** 19 | * Custom drawing for visualizing when a whistle was heard. 20 | * 21 | * @author Felix Thielke 22 | */ 23 | public class WhistleHeard extends PerPlayer { 24 | 25 | @Override 26 | public void draw(final GL2 gl, final RobotState rs, final Camera camera) { 27 | if (rs.getLastTeamMessage() != null 28 | && rs.getLastTeamMessage().valid 29 | && rs.getLastTeamMessage() instanceof BHumanMessage 30 | && rs.getLastGCRDMessage() != null 31 | && rs.getLastGCRDMessage().valid) { 32 | final BHumanMessage bhMsg = (BHumanMessage) rs.getLastTeamMessage(); 33 | final GameControlReturnData gcMsg = rs.getLastGCRDMessage(); 34 | if (!bhMsg.theWhistle.recentWhistle.isEmpty() 35 | && bhMsg.theWhistle.listening 36 | && bhMsg.theWhistle.recentWhistle.get(0).confidenceOfLastWhistleDetection > 0) { 37 | final long timeWhenWhistled = rs.getLastTeamMessageTimestamp() - 38 | bhMsg.theFrameInfo.time.getTimeSince(bhMsg.theWhistle.recentWhistle.get(0).lastTimeWhistleDetected.timestamp); 39 | if (Math.abs(System.currentTimeMillis() - timeWhenWhistled) <= 2000) { 40 | gl.glPushMatrix(); 41 | 42 | if (rs.getPenalty() != PlayerInfo.PENALTY_NONE && !(Rules.league instanceof SPL 43 | && (rs.getPenalty() == PlayerInfo.PENALTY_SPL_ILLEGAL_MOTION_IN_STANDBY 44 | || rs.getPenalty() == PlayerInfo.PENALTY_SPL_ILLEGAL_MOTION_IN_SET))) { 45 | gl.glTranslatef(-bhMsg.playerNumber, -3.5f, 1.f); 46 | } else { 47 | gl.glTranslatef(gcMsg.pose[0] / 1000.f, gcMsg.pose[1] / 1000.f, 1.f); 48 | } 49 | 50 | camera.turnTowardsCamera(gl); 51 | try { 52 | final File f = new File("plugins/" + (rs.getTeamNumber() < 10 ? "0" + rs.getTeamNumber() : String.valueOf(rs.getTeamNumber())) + "/resources/whistle.png").getAbsoluteFile(); 53 | Image.drawImage(gl, TextureLoader.getInstance().loadTexture(gl, f), 0, 0, 0.2f); 54 | } catch (IOException ex) { 55 | JOptionPane.showMessageDialog(null, 56 | "Error loading texture: " + ex.getMessage(), 57 | ex.getClass().getSimpleName(), 58 | JOptionPane.ERROR_MESSAGE); 59 | } 60 | gl.glPopMatrix(); 61 | } 62 | } 63 | } 64 | } 65 | 66 | @Override 67 | public boolean hasAlpha() { 68 | return true; 69 | } 70 | 71 | @Override 72 | public int getPriority() { 73 | return 8; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/Angle.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /** 6 | * Class for angles. 7 | * 8 | * @author Felix Thielke 9 | */ 10 | public class Angle implements SimpleStreamReader { 11 | 12 | /** 13 | * Angle in radians. 14 | */ 15 | public float radians; 16 | 17 | public Angle() { 18 | } 19 | 20 | public Angle(final float radians) { 21 | this.radians = radians; 22 | } 23 | 24 | @Override 25 | public int getStreamedSize() { 26 | return NativeReaders.floatReader.getStreamedSize(); 27 | } 28 | 29 | @Override 30 | public Angle read(ByteBuffer stream) { 31 | radians = NativeReaders.floatReader.read(stream); 32 | return this; 33 | } 34 | 35 | /** 36 | * Returns the value of this angle in degrees. 37 | * 38 | * @return value of this angle in degrees 39 | */ 40 | public float toDegrees() { 41 | return (float) ((double) radians * 180.0 / Math.PI); 42 | } 43 | 44 | public static Angle fromDegrees(final double degrees) { 45 | return new Angle((float) (degrees * Math.PI / 180.0)); 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return toDegrees() + "°"; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/ArrayReader.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import common.Log; 4 | 5 | import java.lang.reflect.InvocationTargetException; 6 | import java.nio.ByteBuffer; 7 | 8 | /** 9 | * StreamReader class for reading fixed-size arrays of streamed objects. 10 | * 11 | * @author Felix Thielke 12 | * @param type of objects stored in the arrays to read 13 | */ 14 | public class ArrayReader implements ProbablySimpleStreamReader { 15 | 16 | private final StreamReader reader; 17 | private final Class> readerClass; 18 | private final T[] array; 19 | 20 | /** 21 | * Constructor. 22 | * 23 | * @param reader object to use to read the elements 24 | */ 25 | public ArrayReader(final StreamReader reader, final T[] array) { 26 | this.reader = reader; 27 | readerClass = null; 28 | this.array = array; 29 | } 30 | 31 | /** 32 | * Constructor. The given class needs to have a constructor that takes no 33 | * arguments. 34 | * 35 | * @param cls class of objects to use to read the elements 36 | */ 37 | public ArrayReader(final Class> cls, final T[] array) { 38 | reader = null; 39 | readerClass = cls; 40 | this.array = array; 41 | } 42 | 43 | @Override 44 | public T[] read(final ByteBuffer stream) { 45 | for (int i = 0; i < array.length; i++) { 46 | if (reader != null) { 47 | array[i] = reader.read(stream); 48 | } else { 49 | try { 50 | array[i] = readerClass.getConstructor().newInstance().read(stream); 51 | } catch (IllegalAccessException 52 | | InstantiationException 53 | | InvocationTargetException 54 | | NoSuchMethodException 55 | | NullPointerException ex) { 56 | Log.error("Failed to instantiate reader class " + readerClass.getName()); 57 | } 58 | } 59 | } 60 | return array; 61 | } 62 | 63 | @Override 64 | public int getStreamedSize(final ByteBuffer stream) { 65 | try { 66 | if (SimpleStreamReader.class.isInstance(reader)) { 67 | return array.length * ((SimpleStreamReader) reader).getStreamedSize(); 68 | } else if (readerClass != null && SimpleStreamReader.class.isAssignableFrom(readerClass)) { 69 | return array.length * ((SimpleStreamReader) readerClass.getConstructor().newInstance()).getStreamedSize(); 70 | } 71 | 72 | final ComplexStreamReader reader = (ComplexStreamReader) (this.reader != null ? this.reader : readerClass.getConstructor().newInstance()); 73 | if (ProbablySimpleStreamReader.class.isInstance(reader) && ProbablySimpleStreamReader.class.cast(reader).isSimpleStreamReader()) { 74 | return array.length * reader.getStreamedSize(stream); 75 | } 76 | 77 | final int position = stream.position(); 78 | int size = 0; 79 | for (int i = 0; i < array.length; i++) { 80 | final int elemSize = reader.getStreamedSize(stream); 81 | size += elemSize; 82 | if (i < array.length - 1) { 83 | if (elemSize > stream.remaining()) { 84 | stream.position(position); 85 | return size + elemSize; 86 | } 87 | stream.position(stream.position() + elemSize); 88 | } 89 | } 90 | stream.position(position); 91 | return size; 92 | } catch (IllegalAccessException 93 | | InstantiationException 94 | | InvocationTargetException 95 | | NoSuchMethodException 96 | | NullPointerException ex) { 97 | Log.error("Failed to instantiate reader class " + readerClass.getName()); 98 | return 0; 99 | } 100 | } 101 | 102 | @Override 103 | public boolean isSimpleStreamReader() { 104 | final StreamReader reader; 105 | try { 106 | reader = this.reader != null ? this.reader : readerClass.getConstructor().newInstance(); 107 | } catch (IllegalAccessException 108 | | InstantiationException 109 | | InvocationTargetException 110 | | NoSuchMethodException 111 | | NullPointerException ex) { 112 | Log.error("Cannot instantiate reader class " + readerClass.getName()); 113 | return false; 114 | } 115 | 116 | return SimpleStreamReader.class.isInstance(reader) 117 | || (ProbablySimpleStreamReader.class.isInstance(reader) && ProbablySimpleStreamReader.class.cast(reader).isSimpleStreamReader()); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/BitStream.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import java.nio.ByteBuffer; 4 | import util.Unsigned; 5 | 6 | /** 7 | * A bitwise stream from which basic data types can be read. 8 | * 9 | * @author Arne Hasselbring 10 | */ 11 | public class BitStream { 12 | 13 | private final ByteBuffer source; 14 | private short currentByte; 15 | private long offset = 0; 16 | 17 | public BitStream(final ByteBuffer source) { 18 | this.source = source; 19 | } 20 | 21 | public long readBits(long bits) { 22 | long result = 0; 23 | for (long i = 0; i < bits; ++i, ++offset) { 24 | if ((offset % 8) == 0) { 25 | currentByte = Unsigned.toUnsigned(source.get()); 26 | } 27 | if((currentByte & (1 << (offset % 8))) != 0) { 28 | result |= (1L << i); 29 | } 30 | } 31 | return result; 32 | } 33 | 34 | public boolean readBoolean() { 35 | return readBits(1) != 0; 36 | } 37 | 38 | public short readChar(long min, long max, int bits) { 39 | if (bits != 0) { 40 | return (short) (readBits(bits) + min); 41 | } else { 42 | return (short) readBits(8); 43 | } 44 | } 45 | 46 | public byte readSignedChar(long min, long max, int bits) { 47 | if (bits != 0) { 48 | return (byte) (readBits(bits) + min); 49 | } else { 50 | return (byte) readBits(8); 51 | } 52 | } 53 | 54 | public short readUnsignedChar(long min, long max, int bits) { 55 | if (bits != 0) { 56 | return (short) (readBits(bits) + min); 57 | } else { 58 | return (short) readBits(8); 59 | } 60 | } 61 | 62 | public short readShort(long min, long max, int bits) { 63 | if (bits != 0) { 64 | return (short) (readBits(bits) + min); 65 | } else { 66 | return (short) readBits(16); 67 | } 68 | } 69 | 70 | public int readUnsignedShort(long min, long max, int bits) { 71 | if (bits != 0) { 72 | return (int) (readBits(bits) + min); 73 | } else { 74 | return (int) readBits(16); 75 | } 76 | } 77 | 78 | public int readInt(long min, long max, int bits) { 79 | if (bits != 0) { 80 | return (int) (readBits(bits) + min); 81 | } else { 82 | return (int) readBits(32); 83 | } 84 | } 85 | 86 | public long readUnsignedInt(long min, long max, int bits) { 87 | if (bits != 0) { 88 | return readBits(bits) + min; 89 | } else { 90 | return readBits(32); 91 | } 92 | } 93 | 94 | public float readFloat(double min, double max, int bits) { 95 | if (bits != 0) { 96 | final long integerValue = readBits(bits); 97 | return (float) (integerValue / (double) ((((long) 1) << bits) - 1) * (max - min) + min); 98 | } else { 99 | return Float.intBitsToFloat((int) readBits(32)); 100 | } 101 | } 102 | 103 | public double readDouble(double min, double max, int bits) { 104 | if (bits != 0) { 105 | final long integerValue = readBits(bits); 106 | return (integerValue / (double) ((((long) 1) << bits) - 1) * (max - min) + min); 107 | } else { 108 | return Double.longBitsToDouble(readBits(64)); 109 | } 110 | } 111 | 112 | public Timestamp readTimestamp(long base, int bits, int shift, int sign, boolean noclip) { 113 | if (sign != 0) { 114 | long integerValue = readBits(bits); 115 | if (integerValue == (((long) 1) << bits) - 1 && noclip) { 116 | return new Timestamp((sign < 0) ? 0 : Long.MAX_VALUE); 117 | } else { 118 | integerValue <<= shift; 119 | return new Timestamp(base + sign * integerValue); 120 | } 121 | } else { 122 | return new Timestamp(readUnsignedInt(0, 0, bits)); 123 | } 124 | } 125 | 126 | public Angle readAngle(int bits) { 127 | return new Angle(readFloat(-Math.PI, Math.PI, bits)); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/ComplexStreamReader.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /** 6 | * Interface for classes whose instances can read fixed-size objects from a 7 | * stream. 8 | * 9 | * @author Felix Thielke 10 | * @param type of the objects that can be read 11 | */ 12 | public interface ComplexStreamReader extends StreamReader { 13 | 14 | /** 15 | * Returns the size in bytes of objects that are read by this object. 16 | * 17 | * @param stream stream to read from 18 | * @return size in bytes 19 | */ 20 | int getStreamedSize(final ByteBuffer stream); 21 | } 22 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/Eigen.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /** 6 | * Abstract class containing classes for Eigen types. 7 | * 8 | * @author Felix Thielke 9 | */ 10 | public abstract class Eigen { 11 | 12 | /** 13 | * Class for a Vector of two ints. 14 | */ 15 | public static class Vector2i extends Vector2 { 16 | 17 | /** 18 | * Constructor. 19 | */ 20 | public Vector2i() { 21 | super(NativeReaders.intReader); 22 | } 23 | 24 | @Override 25 | public Vector2i read(ByteBuffer stream) { 26 | return (Vector2i) super.read(stream); 27 | } 28 | } 29 | 30 | /** 31 | * Class for a Vector of two floats. 32 | */ 33 | public static class Vector2f extends Vector2 { 34 | 35 | /** 36 | * Constructor. 37 | */ 38 | public Vector2f() { 39 | super(NativeReaders.floatReader); 40 | } 41 | 42 | /** 43 | * Constructor. 44 | * 45 | * @param x x value 46 | * @param y y value 47 | */ 48 | public Vector2f(final float x, final float y) { 49 | super(NativeReaders.floatReader); 50 | this.x = x; 51 | this.y = y; 52 | } 53 | 54 | @Override 55 | public Vector2f read(ByteBuffer stream) { 56 | return (Vector2f) super.read(stream); 57 | } 58 | 59 | /** 60 | * Returns the difference of this vector and another. 61 | * 62 | * @param other other vector 63 | * @return difference 64 | */ 65 | public Vector2f diff(final Vector2f other) { 66 | return new Vector2f(other.x - x, other.y - y); 67 | } 68 | 69 | /** 70 | * Scales this vector. 71 | * 72 | * @param factor scaling factor 73 | * @return resulting vector 74 | */ 75 | public Vector2f scale(final float factor) { 76 | return new Vector2f(x * factor, y * factor); 77 | } 78 | 79 | /** 80 | * Inversely scales this vector. 81 | * 82 | * @param factor scaling factor 83 | * @return resulting factor 84 | */ 85 | public Vector2f invScale(final float factor) { 86 | return new Vector2f(x / factor, y / factor); 87 | } 88 | } 89 | 90 | /** 91 | * Class for a Vector of two shorts. 92 | */ 93 | public static class Vector2s extends Vector2 { 94 | 95 | /** 96 | * Constructor. 97 | */ 98 | public Vector2s() { 99 | super(NativeReaders.shortReader); 100 | } 101 | 102 | @Override 103 | public Vector2s read(ByteBuffer stream) { 104 | return (Vector2s) super.read(stream); 105 | } 106 | } 107 | 108 | public static abstract class Vector2 implements SimpleStreamReader> { 109 | 110 | public T x; 111 | public T y; 112 | private final SimpleStreamReader reader; 113 | 114 | public Vector2(final SimpleStreamReader reader) { 115 | this.reader = reader; 116 | } 117 | 118 | @Override 119 | public int getStreamedSize() { 120 | return reader.getStreamedSize() * 2; 121 | } 122 | 123 | @Override 124 | public Vector2 read(final ByteBuffer stream) { 125 | x = reader.read(stream); 126 | y = reader.read(stream); 127 | return this; 128 | } 129 | 130 | /** 131 | * Returns the norm of the vector. 132 | * 133 | * @return norm 134 | */ 135 | public double norm() { 136 | return Math.hypot(x.doubleValue(), y.doubleValue()); 137 | } 138 | } 139 | 140 | public static class Vector3 { 141 | 142 | public T x; 143 | public T y; 144 | public T z; 145 | } 146 | 147 | public static class Vector { 148 | 149 | public T[] elems; 150 | } 151 | 152 | public static class RowMatrix { 153 | 154 | public Vector[] rows; 155 | } 156 | 157 | public static class ColumnMatrix { 158 | 159 | public Vector[] cols; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/EnumMapReader.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import common.Log; 4 | 5 | import java.lang.reflect.InvocationTargetException; 6 | import java.nio.ByteBuffer; 7 | import java.util.EnumMap; 8 | 9 | /** 10 | * 11 | * @author Felix Thielke 12 | */ 13 | public class EnumMapReader, V> implements ProbablySimpleStreamReader> { 14 | 15 | private final Class enumclass; 16 | private final Class> readerclass; 17 | private final StreamReader reader; 18 | 19 | public EnumMapReader(final Class enumclass, final Class> readerclass) { 20 | this.enumclass = enumclass; 21 | this.readerclass = readerclass; 22 | this.reader = null; 23 | } 24 | 25 | public EnumMapReader(final Class enumclass, final StreamReader reader) { 26 | this.enumclass = enumclass; 27 | this.readerclass = null; 28 | this.reader = reader; 29 | } 30 | 31 | @Override 32 | public boolean isSimpleStreamReader() { 33 | final StreamReader reader; 34 | try { 35 | reader = this.reader != null ? this.reader : readerclass.getConstructor().newInstance(); 36 | } catch (IllegalAccessException 37 | | InstantiationException 38 | | InvocationTargetException 39 | | NoSuchMethodException 40 | | NullPointerException ex) { 41 | Log.error("Cannot instantiate reader class " + readerclass.getName()); 42 | return false; 43 | } 44 | 45 | return SimpleStreamReader.class.isInstance(reader) 46 | || (ProbablySimpleStreamReader.class.isInstance(reader) && ProbablySimpleStreamReader.class.cast(reader).isSimpleStreamReader()); 47 | } 48 | 49 | @Override 50 | public int getStreamedSize(final ByteBuffer stream) { 51 | final int count = enumclass.getEnumConstants().length; 52 | 53 | final StreamReader reader; 54 | try { 55 | reader = this.reader != null ? this.reader : readerclass.getConstructor().newInstance(); 56 | } catch (IllegalAccessException 57 | | InstantiationException 58 | | InvocationTargetException 59 | | NoSuchMethodException 60 | | NullPointerException ex) { 61 | Log.error("Cannot instantiate reader class " + readerclass.getName()); 62 | return count; 63 | } 64 | 65 | if (SimpleStreamReader.class.isInstance(reader)) { 66 | return SimpleStreamReader.class.cast(reader).getStreamedSize() * count; 67 | } else if (ProbablySimpleStreamReader.class.isInstance(reader) && ProbablySimpleStreamReader.class.cast(reader).isSimpleStreamReader()) { 68 | return ProbablySimpleStreamReader.class.cast(reader).getStreamedSize(stream) * count; 69 | } 70 | 71 | final int position = stream.position(); 72 | int size = 0; 73 | for (int i = 0; i < count; i++) { 74 | final int elemSize = ComplexStreamReader.class.cast(reader).getStreamedSize(stream); 75 | size += elemSize; 76 | if (i < count - 1) { 77 | if (elemSize > stream.remaining()) { 78 | stream.position(position); 79 | return size + elemSize; 80 | } 81 | stream.position(stream.position() + elemSize); 82 | } 83 | } 84 | stream.position(position); 85 | return size; 86 | } 87 | 88 | @Override 89 | public EnumMap read(final ByteBuffer stream) { 90 | final EnumMap map = new EnumMap<>(enumclass); 91 | for (final K key : enumclass.getEnumConstants()) { 92 | if (reader != null) { 93 | map.put(key, reader.read(stream)); 94 | } else { 95 | try { 96 | map.put(key, readerclass.getConstructor().newInstance().read(stream)); 97 | } catch (IllegalAccessException 98 | | InstantiationException 99 | | InvocationTargetException 100 | | NoSuchMethodException 101 | | NullPointerException ex) { 102 | Log.error("Failed to instantiate reader class " + readerclass.getName()); 103 | } 104 | } 105 | } 106 | return map; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/EnumReader.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /** 6 | * 7 | * @author Felix Thielke 8 | */ 9 | public class EnumReader> implements SimpleStreamReader { 10 | 11 | private final Class enumClass; 12 | 13 | public EnumReader(final Class enumClass) { 14 | this.enumClass = enumClass; 15 | } 16 | 17 | @Override 18 | public int getStreamedSize() { 19 | try { 20 | if (enumClass.getEnumConstants().length >= (1 << 8)) { 21 | return NativeReaders.uintReader.getStreamedSize(); 22 | } 23 | } catch (final SecurityException | IllegalArgumentException ex) { 24 | } 25 | return NativeReaders.ucharReader.getStreamedSize(); 26 | } 27 | 28 | @Override 29 | public E read(final ByteBuffer stream) { 30 | try { 31 | final E[] values = enumClass.getEnumConstants(); 32 | final int val; 33 | if (values.length >= (1 << 8)) { 34 | val = NativeReaders.intReader.read(stream); 35 | } else { 36 | val = NativeReaders.ucharReader.read(stream); 37 | } 38 | if (val < values.length) { 39 | return values[val]; 40 | } 41 | } catch (final SecurityException | IllegalArgumentException ex) { 42 | } 43 | 44 | return null; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/ListCountSize.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 10 | * @author Felix Thielke 11 | */ 12 | @Target(ElementType.FIELD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface ListCountSize { 15 | 16 | int value(); 17 | } 18 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/ListReader.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import common.Log; 4 | 5 | import java.lang.reflect.InvocationTargetException; 6 | import java.nio.ByteBuffer; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import util.Unsigned; 10 | 11 | /** 12 | * StreamReader class for reading lists streamable objects. 13 | * 14 | * @author Felix Thielke 15 | * @param type of objects stored in the arrays to read 16 | */ 17 | public class ListReader implements ComplexStreamReader> { 18 | 19 | private final StreamReader reader; 20 | private final Class> readerClass; 21 | private final int listCountSize; 22 | 23 | /** 24 | * Constructor. 25 | * 26 | * @param reader object to use to read the elements 27 | */ 28 | public ListReader(final StreamReader reader, final int listCountSize) { 29 | this.reader = reader; 30 | readerClass = null; 31 | this.listCountSize = listCountSize; 32 | } 33 | 34 | /** 35 | * Constructor. The given class needs to have a constructor that takes no 36 | * arguments. 37 | * 38 | * @param cls class of objects to use to read the elements 39 | */ 40 | public ListReader(final Class> cls, final int listCountSize) { 41 | reader = null; 42 | readerClass = cls; 43 | this.listCountSize = listCountSize; 44 | } 45 | 46 | /** 47 | * Returns the count of elements in the given streamed array. 48 | * 49 | * @param stream stream 50 | * @return count 51 | */ 52 | public int getElementCount(final ByteBuffer stream) { 53 | switch (listCountSize) { 54 | case 1: 55 | return Unsigned.toUnsigned(stream.get(stream.position())); 56 | case 2: 57 | return Unsigned.toUnsigned(stream.getShort(stream.position())); 58 | case 4: 59 | return stream.getInt(stream.position()); 60 | default: 61 | Log.error("List count size " + listCountSize + " is not allowed!"); 62 | return 0; 63 | } 64 | } 65 | 66 | @Override 67 | public List read(final ByteBuffer stream) { 68 | final int count = getElementCount(stream); 69 | stream.position(stream.position() + listCountSize); 70 | final ArrayList elems = new ArrayList<>(count); 71 | for (int i = 0; i < count; i++) { 72 | if (reader != null) { 73 | elems.add(reader.read(stream)); 74 | } else { 75 | try { 76 | elems.add(readerClass.getConstructor().newInstance().read(stream)); 77 | } catch (IllegalAccessException 78 | | InstantiationException 79 | | InvocationTargetException 80 | | NoSuchMethodException 81 | | NullPointerException ex) { 82 | } 83 | } 84 | } 85 | return elems; 86 | } 87 | 88 | @Override 89 | public int getStreamedSize(final ByteBuffer stream) { 90 | final int count = getElementCount(stream); 91 | try { 92 | if (SimpleStreamReader.class.isInstance(reader)) { 93 | return listCountSize + count * ((SimpleStreamReader) reader).getStreamedSize(); 94 | } else if (readerClass != null && SimpleStreamReader.class.isAssignableFrom(readerClass)) { 95 | return listCountSize + count * ((SimpleStreamReader) readerClass.getConstructor().newInstance()).getStreamedSize(); 96 | } 97 | 98 | final ComplexStreamReader reader = (ComplexStreamReader) (this.reader != null ? this.reader : readerClass.getConstructor().newInstance()); 99 | if (ProbablySimpleStreamReader.class.isInstance(reader) && ProbablySimpleStreamReader.class.cast(reader).isSimpleStreamReader()) { 100 | return listCountSize + count * reader.getStreamedSize(stream); 101 | } 102 | 103 | final int position = stream.position(); 104 | stream.position(stream.position() + listCountSize); 105 | int size = listCountSize; 106 | for (int i = 0; i < count; i++) { 107 | final int elemSize = reader.getStreamedSize(stream); 108 | size += elemSize; 109 | if (i < count - 1) { 110 | if (elemSize > stream.remaining()) { 111 | stream.position(position); 112 | return size + elemSize; 113 | } 114 | stream.position(stream.position() + elemSize); 115 | } 116 | } 117 | stream.position(position); 118 | return size; 119 | } catch (IllegalAccessException 120 | | InstantiationException 121 | | InvocationTargetException 122 | | NoSuchMethodException 123 | | NullPointerException ex) { 124 | Log.error("Failed to instantiate reader class " + readerClass.getName()); 125 | return 0; 126 | } 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/Primitive.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 10 | * @author Felix Thielke 11 | */ 12 | @Target(ElementType.FIELD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface Primitive { 15 | 16 | String value(); 17 | } 18 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/ProbablySimpleStreamReader.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | /** 4 | * Interface for classes whose instances are maybe SimpleStreamReaders although 5 | * they implement the ComplexStreamReader interface. 6 | * 7 | * @author Felix Thielke 8 | * @param type of the objects that can be read 9 | */ 10 | public interface ProbablySimpleStreamReader extends ComplexStreamReader { 11 | 12 | /** 13 | * Returns whether this object qualifies as a SimpleStreamReader. 14 | * 15 | * @return bool 16 | */ 17 | boolean isSimpleStreamReader(); 18 | } 19 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/Reader.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 10 | * @author Felix Thielke 11 | */ 12 | @Target(ElementType.FIELD) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | public @interface Reader { 15 | 16 | Class> value(); 17 | } 18 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/SimpleStreamReader.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | /** 4 | * Interface for classes whose instances can read fixed-size objects from a 5 | * stream. 6 | * 7 | * @author Felix Thielke 8 | * @param type of the objects that can be read 9 | */ 10 | public interface SimpleStreamReader extends StreamReader { 11 | 12 | /** 13 | * Returns the size in bytes of objects that are read by this object. 14 | * 15 | * @return size in bytes 16 | */ 17 | int getStreamedSize(); 18 | } 19 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/StreamReader.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /** 6 | * Interface for classes whose instances can read objects from a stream. 7 | * 8 | * @author Felix Thielke 9 | * @param type of the objects that can be read 10 | */ 11 | public interface StreamReader { 12 | 13 | /** 14 | * Reads an object from the given stream. 15 | * 16 | * @param stream stream to read from 17 | * @return object 18 | */ 19 | T read(final ByteBuffer stream); 20 | } 21 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/bhuman/message/data/Timestamp.java: -------------------------------------------------------------------------------- 1 | package bhuman.message.data; 2 | 3 | import java.nio.ByteBuffer; 4 | import util.Unsigned; 5 | 6 | /** 7 | * 8 | * @author Felix Thielke 9 | */ 10 | public class Timestamp implements SimpleStreamReader { 11 | 12 | public long timestamp; 13 | 14 | public Timestamp() { 15 | } 16 | 17 | public Timestamp(final long timestamp) { 18 | this.timestamp = timestamp; 19 | } 20 | 21 | @Override 22 | public int getStreamedSize() { 23 | return 4; 24 | } 25 | 26 | @Override 27 | public Timestamp read(final ByteBuffer stream) { 28 | timestamp = Unsigned.toUnsigned(stream.getInt()); 29 | return this; 30 | } 31 | 32 | public long getTimeSince(final long other) { 33 | return timestamp - other; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /resources/plugins/05/B-Human/src/util/Unsigned.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | /** 4 | * Utility class for getting unsigned from signed values. 5 | * 6 | * @author Felix Thielke 7 | */ 8 | public class Unsigned { 9 | 10 | /** 11 | * Convert the given byte value to an unsigned byte. 12 | * 13 | * @param b signed value 14 | * @return unsigned value 15 | */ 16 | public static short toUnsigned(final byte b) { 17 | return (short) (b < 0 ? b + (1 << Byte.SIZE) : b); 18 | } 19 | 20 | /** 21 | * Convert the given short value to an unsigned short. 22 | * 23 | * @param s signed value 24 | * @return unsigned value 25 | */ 26 | public static int toUnsigned(final short s) { 27 | return (s < 0 ? s + (1 << Short.SIZE) : s); 28 | } 29 | 30 | /** 31 | * Convert the given int value to an unsigned int. 32 | * 33 | * @param i signed value 34 | * @return unsigned value 35 | */ 36 | public static long toUnsigned(final int i) { 37 | return i < 0 ? i + (1L << (long) Integer.SIZE) : i; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /resources/plugins/05/resources/fire!_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/plugins/05/resources/fire!_icon.png -------------------------------------------------------------------------------- /resources/plugins/05/resources/fire_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/plugins/05/resources/fire_icon.png -------------------------------------------------------------------------------- /resources/plugins/05/resources/heat_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/plugins/05/resources/heat_icon.png -------------------------------------------------------------------------------- /resources/plugins/05/resources/kickInLeft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/plugins/05/resources/kickInLeft.png -------------------------------------------------------------------------------- /resources/plugins/05/resources/kickInRight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/plugins/05/resources/kickInRight.png -------------------------------------------------------------------------------- /resources/plugins/05/resources/ready.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/plugins/05/resources/ready.png -------------------------------------------------------------------------------- /resources/plugins/05/resources/whistle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/plugins/05/resources/whistle.png -------------------------------------------------------------------------------- /resources/scene/Ball2010SPL.rsi2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /resources/scene/TeamComm.ros2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /resources/scene/Textures/ball_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/scene/Textures/ball_icon.png -------------------------------------------------------------------------------- /resources/scene/Textures/net.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/scene/Textures/net.png -------------------------------------------------------------------------------- /resources/scene/Textures/shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/scene/Textures/shadow.png -------------------------------------------------------------------------------- /resources/scene/Textures/textures-button_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/scene/Textures/textures-button_gray.png -------------------------------------------------------------------------------- /resources/scene/Textures/textures-ear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboCup-SPL/GameController/aeebe925783117087fa137df01b73e67e9d87d3b/resources/scene/Textures/textures-ear.png -------------------------------------------------------------------------------- /src/common/ApplicationLock.java: -------------------------------------------------------------------------------- 1 | package common; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.nio.channels.FileChannel; 7 | import java.nio.channels.FileLock; 8 | 9 | /** 10 | * @author Marcel Steinbeck 11 | * 12 | * This class is used to ensure, that only one instance of an application exists. 13 | */ 14 | public class ApplicationLock { 15 | /** The lockFile. */ 16 | private final File lockFile; 17 | 18 | /** The acquire. */ 19 | private FileLock lock; 20 | 21 | /** The lockChannel. */ 22 | private FileChannel lockChannel; 23 | 24 | /** The lockStream. */ 25 | private FileOutputStream lockStream; 26 | 27 | /** 28 | * Creates a new ApplicationLock instance. 29 | * Every application instance gets it own key. 30 | * 31 | * @param key the key of the lock 32 | */ 33 | public ApplicationLock(String key) { 34 | // ensure the path ends with system dependent file-separator 35 | String tmp_dir = System.getProperty("java.io.tmpdir"); 36 | if (!tmp_dir.endsWith(System.getProperty("file.separator"))) { 37 | tmp_dir += System.getProperty("file.separator"); 38 | } 39 | 40 | // create lock-file in tmp-dir 41 | lockFile = new File(tmp_dir + key + ".app_lock"); 42 | lockFile.deleteOnExit(); 43 | } 44 | 45 | /** 46 | * Acquires a the lock. 47 | * 48 | * @return true if no other application acquired a lock before, false otherwise 49 | * @throws IOException if an error occurred while trying to lock 50 | */ 51 | public boolean acquire() throws IOException { 52 | lockStream = new FileOutputStream(lockFile); 53 | lockChannel = lockStream.getChannel(); 54 | lock = lockChannel.tryLock(); 55 | return null != lock; 56 | 57 | } 58 | 59 | /** 60 | * Releases the lock 61 | * 62 | * @throws IOException if an error occurred while trying to unlock 63 | */ 64 | public void release() throws IOException { 65 | if (lock.isValid()) { 66 | lock.release(); 67 | } 68 | if (lockStream != null) { 69 | lockStream.close(); 70 | } 71 | if (lockChannel.isOpen()) { 72 | lockChannel.close(); 73 | } 74 | lockFile.delete(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/common/Log.java: -------------------------------------------------------------------------------- 1 | package common; 2 | 3 | import java.io.FileWriter; 4 | import java.io.IOException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | 8 | 9 | /** 10 | * @author Michel Bartsch 11 | * 12 | * This class should be used to log into a log file. A new file will be created 13 | * every time the GameController is started. 14 | * 15 | * This class is a singleton! 16 | */ 17 | public class Log 18 | { 19 | /** The instance of the singleton. */ 20 | private static final Log instance = new Log(); 21 | 22 | /** The error-file to write into. */ 23 | private FileWriter errorFile; 24 | /** The file to write into. */ 25 | private final String errorPath = "error.txt"; 26 | 27 | /** The format of timestamps. */ 28 | public static final SimpleDateFormat timestampFormat = new SimpleDateFormat("yyyy.M.dd-kk.mm.ss"); 29 | 30 | /** 31 | * Creates a new Log. 32 | */ 33 | private Log() {} 34 | 35 | /** 36 | * Writes a line, beginning with a timestamp, in the error-file and creates 37 | * a new one, if it does not yet exist. 38 | * 39 | * This can be used before initialising the log! 40 | * 41 | * @param s The string to be written in the error-file. 42 | */ 43 | public static void error(String s) 44 | { 45 | System.err.println(s); 46 | try { 47 | if (instance.errorFile == null) { 48 | instance.errorFile = new FileWriter(instance.errorPath); 49 | } 50 | instance.errorFile.write(timestampFormat.format(new Date(System.currentTimeMillis()))+": "+s+"\n"); 51 | instance.errorFile.flush(); 52 | } catch (IOException e) { 53 | System.err.println("cannot write to error file!"); 54 | } 55 | } 56 | 57 | /** 58 | * Closes the Log 59 | * 60 | * @throws IOException if an error occurred while trying to close the FileWriters 61 | */ 62 | public static void close() throws IOException { 63 | if (instance.errorFile != null) { 64 | instance.errorFile.close(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/common/net/GameControlReturnDataPackage.java: -------------------------------------------------------------------------------- 1 | package common.net; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Class for a message. Instances of this class are stored in the log files. 7 | * 8 | * @author Felix Thielke 9 | */ 10 | public class GameControlReturnDataPackage implements Serializable { 11 | 12 | private static final long serialVersionUID = 3253050821684309808L; 13 | 14 | /** 15 | * Host address from which this message was received. 16 | */ 17 | public final String host; 18 | 19 | /** 20 | * Raw message data. 21 | */ 22 | public final byte[] message; 23 | 24 | /** 25 | * Constructor. 26 | * 27 | * @param host host address from which this message was received 28 | * @param message raw message data 29 | */ 30 | public GameControlReturnDataPackage(final String host, final byte[] message) { 31 | this.host = host; 32 | this.message = message; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/common/net/SPLTeamMessagePackage.java: -------------------------------------------------------------------------------- 1 | package common.net; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Class for a message. Instances of this class are stored in the log files. 7 | * 8 | * @author Felix Thielke 9 | */ 10 | public class SPLTeamMessagePackage implements Serializable { 11 | 12 | private static final long serialVersionUID = 758311663011901849L; 13 | 14 | /** 15 | * Host address from which this message was received. 16 | */ 17 | public final String host; 18 | 19 | /** 20 | * Number of the team to which the port belongs on which this message was 21 | * received. 22 | */ 23 | public final int team; 24 | 25 | /** 26 | * Raw message data. 27 | */ 28 | public final byte[] message; 29 | 30 | /** 31 | * Constructor. 32 | * 33 | * @param host host address from which this message was received 34 | * @param team number of the team to which the port belongs on which this 35 | * message was received 36 | * @param message raw message data 37 | */ 38 | public SPLTeamMessagePackage(final String host, final int team, final byte[] message) { 39 | this.host = host; 40 | this.team = team; 41 | this.message = message; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/data/PlayerInfo.java: -------------------------------------------------------------------------------- 1 | package data; 2 | 3 | import java.io.Serializable; 4 | import java.nio.ByteBuffer; 5 | import java.nio.ByteOrder; 6 | 7 | /** 8 | * This class is part of the data which are sent to the robots. It just 9 | * represents this data, reads and writes between C-structure and Java, nothing 10 | * more. 11 | * 12 | * @author Michel Bartsch 13 | */ 14 | public class PlayerInfo implements Serializable { 15 | 16 | private static final long serialVersionUID = -8480462279073509072L; 17 | 18 | /** 19 | * What type of penalty a player may have. 20 | */ 21 | public static final byte PENALTY_NONE = 0; 22 | 23 | public static final byte PENALTY_SPL_ILLEGAL_BALL_CONTACT = 1; 24 | public static final byte PENALTY_SPL_PLAYER_PUSHING = 2; 25 | public static final byte PENALTY_SPL_ILLEGAL_MOTION_IN_SET = 3; 26 | public static final byte PENALTY_SPL_INACTIVE_PLAYER = 4; 27 | public static final byte PENALTY_SPL_ILLEGAL_POSITION = 5; 28 | public static final byte PENALTY_SPL_LEAVING_THE_FIELD = 6; 29 | public static final byte PENALTY_SPL_REQUEST_FOR_PICKUP = 7; 30 | public static final byte PENALTY_SPL_LOCAL_GAME_STUCK = 8; 31 | public static final byte PENALTY_SPL_ILLEGAL_POSITION_IN_SET = 9; 32 | public static final byte PENALTY_SPL_PLAYER_STANCE = 10; 33 | public static final byte PENALTY_SPL_ILLEGAL_MOTION_IN_STANDBY = 11; 34 | 35 | public static final byte PENALTY_SUBSTITUTE = 14; 36 | public static final byte PENALTY_MANUAL = 15; 37 | 38 | /** 39 | * The size in bytes this class has packed. 40 | */ 41 | public static final int SIZE 42 | = 1 43 | + // penalty 44 | 1; // secsTillUnpenalised 45 | 46 | //this is streamed 47 | public byte penalty = PENALTY_NONE; // penalty state of the player 48 | public byte secsTillUnpenalised; // estimate of time till unpenalised 49 | 50 | /** 51 | * Packing this Java class to the C-structure to be send. 52 | * 53 | * @return Byte array representing the C-structure. 54 | */ 55 | public byte[] toByteArray() { 56 | ByteBuffer buffer = ByteBuffer.allocate(SIZE); 57 | buffer.order(ByteOrder.LITTLE_ENDIAN); 58 | buffer.put(penalty); 59 | buffer.put(secsTillUnpenalised); 60 | return buffer.array(); 61 | } 62 | 63 | /** 64 | * Unpacking the C-structure to the Java class. 65 | * 66 | * @param buffer The buffered C-structure. 67 | */ 68 | public void fromByteArray(ByteBuffer buffer) { 69 | buffer.order(ByteOrder.LITTLE_ENDIAN); 70 | penalty = buffer.get(); 71 | secsTillUnpenalised = buffer.get(); 72 | } 73 | 74 | public static String getPenaltyName(int penalty) { 75 | switch (penalty) { 76 | case PENALTY_NONE: 77 | return "none"; 78 | case PENALTY_SPL_ILLEGAL_BALL_CONTACT: 79 | return "illegal ball contact"; 80 | case PENALTY_SPL_PLAYER_PUSHING: 81 | return "pushing"; 82 | case PENALTY_SPL_ILLEGAL_MOTION_IN_SET: 83 | return "illegal motion in set"; 84 | case PENALTY_SPL_INACTIVE_PLAYER: 85 | return "inactive"; 86 | case PENALTY_SPL_ILLEGAL_POSITION: 87 | return "illegal position"; 88 | case PENALTY_SPL_LEAVING_THE_FIELD: 89 | return "leaving the field"; 90 | case PENALTY_SPL_REQUEST_FOR_PICKUP: 91 | return "request for pickup"; 92 | case PENALTY_SPL_LOCAL_GAME_STUCK: 93 | return "local game stuck"; 94 | case PENALTY_SPL_ILLEGAL_POSITION_IN_SET: 95 | return "illegal position in set"; 96 | case PENALTY_SPL_PLAYER_STANCE: 97 | return "player stance"; 98 | case PENALTY_SPL_ILLEGAL_MOTION_IN_STANDBY: 99 | return "illegal motion in standby"; 100 | case PENALTY_SUBSTITUTE: 101 | return "substitute"; 102 | case PENALTY_MANUAL: 103 | return "manual"; 104 | default: 105 | return "undefined(" + penalty + ")"; 106 | } 107 | } 108 | 109 | @Override 110 | public String toString() { 111 | String out = ""; 112 | String temp = getPenaltyName(penalty); 113 | out += " penalty: " + temp + "\n"; 114 | out += "secsTillUnpenalised: " + secsTillUnpenalised + "\n"; 115 | return out; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/data/Rules.java: -------------------------------------------------------------------------------- 1 | package data; 2 | 3 | import java.awt.Color; 4 | 5 | 6 | /** 7 | * This class holds attributes defining rules. 8 | * 9 | * @author Michel Bartsch 10 | */ 11 | public abstract class Rules 12 | { 13 | /** Note all league's rules here to have them available. */ 14 | public static final Rules[] LEAGUES = { 15 | new SPL() 16 | }; 17 | 18 | /** 19 | * Returns the Rules object for the given class. 20 | */ 21 | public static Rules getLeagueRules(final Class c) { 22 | for(final Rules r : LEAGUES) { 23 | if(c.isInstance(r) && r.getClass().isAssignableFrom(c)) { 24 | return r; 25 | } 26 | } 27 | return null; 28 | } 29 | 30 | /** The rules of the league playing. */ 31 | public static Rules league = LEAGUES[0]; 32 | /** The league's name this rules are for. */ 33 | public String leagueName; 34 | /** The league's directory name with its teams and icons. */ 35 | public String leagueDirectory; 36 | /** How many robots are in a team. */ 37 | public int teamSize; 38 | /** The Java Colors the left and the right team starts with. */ 39 | public Color[] teamColor; 40 | /** Time in seconds one half is long. */ 41 | public int halfTime; 42 | } 43 | -------------------------------------------------------------------------------- /src/data/SPL.java: -------------------------------------------------------------------------------- 1 | package data; 2 | 3 | import java.awt.Color; 4 | 5 | /** 6 | * This class sets attributes given by the spl rules. 7 | * 8 | * @author Michel-Zen 9 | */ 10 | public class SPL extends Rules 11 | { 12 | SPL() 13 | { 14 | /* The league's name this rules are for. */ 15 | leagueName = "SPL"; 16 | /* The league's directory name with its teams and icons. */ 17 | leagueDirectory = "spl"; 18 | /* How many robots are in a team. */ 19 | teamSize = 6; // 5 players + 1 sub 20 | /* The Java Colors the left and the right team starts with. */ 21 | teamColor = new Color[] {Color.BLUE, Color.RED, new Color(224, 200, 0), Color.BLACK, Color.WHITE, new Color(0, 192, 0), new Color(255, 165, 0), new Color(128, 0, 128), new Color(165, 42, 42), new Color(128, 128, 128)}; 22 | /* Time in seconds one half is long. */ 23 | halfTime = 10*60; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/data/SPLTeamMessage.java: -------------------------------------------------------------------------------- 1 | package data; 2 | 3 | import java.io.Serializable; 4 | import java.nio.ByteBuffer; 5 | 6 | public class SPLTeamMessage implements Serializable { 7 | 8 | private static final long serialVersionUID = 2204681477211322628L; 9 | 10 | public static final int MAX_SIZE = 128; 11 | 12 | public byte[] data; 13 | 14 | public boolean valid; 15 | 16 | public static SPLTeamMessage createFrom(final SPLTeamMessage message) { 17 | final SPLTeamMessage m = new SPLTeamMessage(); 18 | m.data = message.data; 19 | m.valid = message.valid; 20 | return m; 21 | } 22 | 23 | public boolean fromByteArray(ByteBuffer buffer) { 24 | valid = true; 25 | try { 26 | if (buffer.remaining() > MAX_SIZE) { 27 | valid = false; 28 | } 29 | data = new byte[Math.min(buffer.remaining(), MAX_SIZE)]; 30 | buffer.get(data, 0, data.length); 31 | } catch (RuntimeException e) { 32 | valid = false; 33 | } 34 | 35 | return valid; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/data/TrueDataRequest.java: -------------------------------------------------------------------------------- 1 | package data; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.ByteOrder; 5 | 6 | /** 7 | * Class for a message which can be sent to the GameController in order to 8 | * request true game state information. This can be used for displaying the true 9 | * game state in the TeamCommunicationMonitor/GameStateVisualizer and similar 10 | * applications. See teamcomm.net.GameControlDataReceiver for a reference 11 | * implementation. 12 | * 13 | * @author Felix Thielke 14 | */ 15 | public class TrueDataRequest { 16 | 17 | public static final int GAMECONTROLLER_TRUEDATAREQUEST_PORT = 3636; // port to receive requests for true game state data on 18 | 19 | public static final String TRUEDATAREQUEST_HEADER = "RGTr"; 20 | public static final byte TRUEDATAREQUEST_VERSION = 0; 21 | public static final byte TRUEDATAREQUEST_SIZE = 4 + 1; 22 | 23 | public String header; 24 | public byte version; 25 | 26 | public static TrueDataRequest createRequest() { 27 | final TrueDataRequest request = new TrueDataRequest(); 28 | request.header = TRUEDATAREQUEST_HEADER; 29 | request.version = TRUEDATAREQUEST_VERSION; 30 | return request; 31 | } 32 | 33 | public boolean fromByteArray(final ByteBuffer buf) { 34 | buf.order(ByteOrder.LITTLE_ENDIAN); 35 | final byte[] headerBytes = new byte[4]; 36 | buf.get(headerBytes); 37 | header = new String(headerBytes); 38 | version = buf.get(); 39 | return !buf.hasRemaining() && header.equals(TRUEDATAREQUEST_HEADER) && version == TRUEDATAREQUEST_VERSION; 40 | } 41 | 42 | public byte[] toByteArray() { 43 | final ByteBuffer buf = ByteBuffer.allocate(TRUEDATAREQUEST_SIZE); 44 | buf.order(ByteOrder.LITTLE_ENDIAN); 45 | buf.put(header.getBytes()); 46 | buf.put(version); 47 | return buf.array(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/eventrecorder/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: eventrecorder.EventRecorder 3 | Author: Andre Muehlenbrock 4 | -------------------------------------------------------------------------------- /src/eventrecorder/action/Action.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.action; 2 | 3 | import eventrecorder.data.LogEntry; 4 | 5 | /** 6 | * Abstract class of the Command pattern. 7 | * 8 | * @author Andre Muehlenbrock 9 | */ 10 | 11 | public abstract class Action { 12 | protected final LogEntry entry; 13 | private final boolean addToHistory; 14 | 15 | /** 16 | * Constructor. 17 | * 18 | * @param entry 19 | * @param addToHistory 20 | */ 21 | 22 | public Action(LogEntry entry, boolean addToHistory){ 23 | this.entry = entry; 24 | this.addToHistory = addToHistory; 25 | } 26 | 27 | 28 | /** 29 | * Execute and redo functionality of the action 30 | * @return true if it worked. 31 | */ 32 | 33 | public abstract boolean executeAction(); 34 | 35 | /** 36 | * undo functionality of the action 37 | * @return true if it worked. 38 | */ 39 | 40 | public abstract boolean undoAction(); 41 | 42 | /** 43 | * @return Returns the effected LogEntry. 44 | */ 45 | 46 | public LogEntry getAffectedLogEntry(){ 47 | return entry; 48 | } 49 | 50 | public boolean shouldBeAddedToHistory(){ 51 | return addToHistory; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/eventrecorder/action/ActionHistory.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.action; 2 | 3 | import eventrecorder.EventRecorder; 4 | 5 | /** 6 | * Saves all executed actions for undo- and redo-functionality. 7 | * 8 | * @author Andre Muehlenbrock 9 | */ 10 | 11 | public class ActionHistory { 12 | public final CircularLiFoBuffer actions = new CircularLiFoBuffer<>(1000); 13 | 14 | public boolean execute(Action action){ 15 | if(action.executeAction()){ 16 | 17 | if(action.shouldBeAddedToHistory()) 18 | actions.push(action); 19 | 20 | notifyGUI(action); 21 | System.out.println("Execute: "+action.getClass().getSimpleName()); 22 | return true; 23 | } 24 | 25 | return false; 26 | } 27 | 28 | /** 29 | * Undoes the last action. 30 | * 31 | * @return true if it worked. 32 | */ 33 | 34 | public boolean undo(){ 35 | if(actions.isEmpty()) 36 | return false; 37 | 38 | Action action = actions.pop(); 39 | 40 | System.out.println("Undo: "+action.getClass().getSimpleName()); 41 | if(action.undoAction()){ 42 | notifyGUI(action); 43 | return true; 44 | } 45 | 46 | return false; 47 | } 48 | 49 | /** 50 | * Redo the last undone action. 51 | * 52 | * @return true if it worked. 53 | */ 54 | 55 | public boolean redo(){ 56 | if(!actions.hasNext()) 57 | return false; 58 | 59 | 60 | Action action = actions.popForward(); 61 | System.out.println("Redo: "+action.getClass().getSimpleName()); 62 | if(action.executeAction()){ 63 | notifyGUI(action); 64 | return true; 65 | } 66 | 67 | return false; 68 | } 69 | 70 | 71 | /** 72 | * Notify the GUI that a LogEntry is changed. 73 | * 74 | * @param a 75 | */ 76 | 77 | private void notifyGUI(Action a){ 78 | EventRecorder.gui.actionWasExecuted(a); 79 | } 80 | 81 | public boolean undoPossible(){ 82 | return !actions.isEmpty(); 83 | } 84 | 85 | public boolean redoPossible(){ 86 | return actions.hasNext(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/eventrecorder/action/CircularLiFoBuffer.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.action; 2 | 3 | /** 4 | * Circular-Last-In-First-Out-Buffer specially for redo/undo-functionality. 5 | * 6 | * @author Andre Muehlenbrock 7 | */ 8 | 9 | public class CircularLiFoBuffer { 10 | final T[] array; 11 | int pos = -1; 12 | int elements = 0; 13 | int pops = 0; 14 | 15 | @SuppressWarnings("unchecked") 16 | public CircularLiFoBuffer(int size){ 17 | array = (T[]) new Object[size]; 18 | } 19 | 20 | public T get(int p){ 21 | return array[(array.length+pos-p)%array.length]; 22 | } 23 | 24 | public void push(T t){ 25 | pops = 0; 26 | pos++; 27 | 28 | if(pos >= array.length){ 29 | pos = 0; 30 | } 31 | 32 | array[pos] = t; 33 | 34 | 35 | if(elements < array.length){ 36 | elements++; 37 | } 38 | } 39 | 40 | public T peek(){ 41 | if(elements > 0){ 42 | return array[pos]; 43 | } 44 | 45 | return null; 46 | } 47 | 48 | public T pop(){ 49 | T t = peek(); 50 | if(t != null){ 51 | pops++; 52 | elements--; 53 | pos--; 54 | if(pos < 0) 55 | pos=array.length-1; 56 | return t; 57 | } 58 | return null; 59 | } 60 | 61 | public T popForward(){ 62 | if(pops > 0){ 63 | pops--; 64 | elements++; 65 | pos++; 66 | if(pos >= array.length){ 67 | pos = 0; 68 | } 69 | return array[pos]; 70 | } 71 | 72 | return null; 73 | } 74 | 75 | public boolean isEmpty(){ 76 | return elements <= 0; 77 | } 78 | 79 | public boolean hasNext(){ 80 | return pops > 0; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/eventrecorder/action/EntryChangeTextAction.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.action; 2 | 3 | import eventrecorder.data.LogEntry; 4 | 5 | /** 6 | * Changes the text value of a LogEntry 7 | * 8 | * @author Andre Muehlenbrock 9 | */ 10 | 11 | public class EntryChangeTextAction extends Action{ 12 | private final String newText; 13 | private final String savedText; 14 | 15 | /** 16 | * Creates a new ChangeAction. 17 | * 18 | * @param entry LogEntry in the DataModel. 19 | * @param newText entry's text. 20 | * @param savedText entry's previous text. 21 | */ 22 | 23 | public EntryChangeTextAction(LogEntry entry, String newText, String savedText){ 24 | super(entry, true); 25 | this.newText = newText; 26 | this.savedText = savedText; 27 | } 28 | 29 | @Override 30 | public boolean executeAction() { 31 | entry.text = newText; 32 | return true; 33 | } 34 | 35 | @Override 36 | public boolean undoAction() { 37 | entry.text = savedText; 38 | return true; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/eventrecorder/action/EntryChangeTimeAction.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.action; 2 | 3 | import eventrecorder.data.LogEntry; 4 | 5 | /** 6 | * Changes the time value of a LogEntry 7 | * 8 | * @author Andre Muehlenbrock 9 | */ 10 | 11 | public class EntryChangeTimeAction extends Action{ 12 | private final String newTime; 13 | private final String savedTime; 14 | private final boolean wasFirstTimeSet; 15 | 16 | /** 17 | * Creates a new ChangeAction. 18 | * 19 | * @param entry LogEntry in the DataModel. 20 | * @param newTime New time value. 21 | * @param savedTime Previous time value to support undo. 22 | */ 23 | 24 | public EntryChangeTimeAction(LogEntry entry, String newTime, String savedTime){ 25 | super(entry, true); 26 | this.newTime = newTime; 27 | this.savedTime = savedTime; 28 | wasFirstTimeSet = entry.firstTimeSet; 29 | } 30 | 31 | @Override 32 | public boolean executeAction() { 33 | entry.time = newTime; 34 | entry.firstTimeSet = false; 35 | return true; 36 | } 37 | 38 | @Override 39 | public boolean undoAction() { 40 | entry.time = savedTime; 41 | entry.firstTimeSet = wasFirstTimeSet; 42 | return true; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/eventrecorder/action/EntryCreateAction.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.action; 2 | 3 | import eventrecorder.EventRecorder; 4 | import eventrecorder.data.LogEntry; 5 | 6 | /** 7 | * Creates a LogEntry 8 | * 9 | * @author Andre Muehlenbrock 10 | */ 11 | 12 | public class EntryCreateAction extends Action{ 13 | private int position; 14 | 15 | /** 16 | * Creates a CreateAction which adds the entry to the end of the list. 17 | * 18 | * @param entry LogEntry 19 | */ 20 | 21 | public EntryCreateAction(LogEntry entry){ 22 | this(entry, EventRecorder.model.logEntries.size(), true); 23 | } 24 | 25 | 26 | public EntryCreateAction(LogEntry entry, boolean addToHistory){ 27 | this(entry, EventRecorder.model.logEntries.size(), addToHistory); 28 | } 29 | 30 | public EntryCreateAction(LogEntry entry, int position){ 31 | super(entry, true); 32 | this.position = position; 33 | } 34 | 35 | /** 36 | * Creates a CreateAction which adds the entry to the given position of the list. 37 | * 38 | * @param entry LogEntry 39 | * @param position LogEntry position. 40 | */ 41 | 42 | public EntryCreateAction(LogEntry entry, int position, boolean addToHistory){ 43 | super(entry, addToHistory); 44 | this.position = position; 45 | } 46 | 47 | @Override 48 | public boolean executeAction() { 49 | EventRecorder.model.logEntries.add(position,entry); 50 | return true; 51 | } 52 | 53 | @Override 54 | public boolean undoAction() { 55 | position = EventRecorder.model.logEntries.indexOf(entry); 56 | if(position == -1) 57 | return false; 58 | return EventRecorder.model.logEntries.remove(position) != null; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/eventrecorder/action/EntryDeleteAction.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.action; 2 | 3 | import eventrecorder.EventRecorder; 4 | import eventrecorder.data.LogEntry; 5 | 6 | /** 7 | * Action for delete a logEntry. 8 | * 9 | * @author Andre Muehlenbrock 10 | */ 11 | public class EntryDeleteAction extends Action{ 12 | private int savedPosition; 13 | 14 | public EntryDeleteAction(LogEntry entry) { 15 | super(entry, true); 16 | } 17 | 18 | @Override 19 | public boolean executeAction() { 20 | savedPosition = EventRecorder.model.logEntries.indexOf(entry); 21 | return EventRecorder.model.logEntries.remove(savedPosition) != null; 22 | } 23 | 24 | @Override 25 | public boolean undoAction() { 26 | EventRecorder.model.logEntries.add(savedPosition, entry); 27 | return true; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/eventrecorder/action/EntryTypeChangeAction.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.action; 2 | 3 | import eventrecorder.data.LogEntry; 4 | import eventrecorder.data.LogType; 5 | 6 | public class EntryTypeChangeAction extends Action{ 7 | private final LogType savedLogType; 8 | private final LogType newLogType; 9 | 10 | public EntryTypeChangeAction(LogEntry entry, LogType newLogType, LogType savedLogType){ 11 | this(entry, true, newLogType, savedLogType); 12 | } 13 | 14 | public EntryTypeChangeAction(LogEntry entry, boolean addToHistory, LogType newLogType, LogType savedLogType) { 15 | super(entry, addToHistory); 16 | this.savedLogType = savedLogType; 17 | this.newLogType = newLogType; 18 | } 19 | 20 | @Override 21 | public boolean executeAction() { 22 | entry.type = newLogType; 23 | return true; 24 | } 25 | 26 | @Override 27 | public boolean undoAction() { 28 | entry.type = savedLogType; 29 | return true; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/eventrecorder/action/TitleChangeAction.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.action; 2 | 3 | import eventrecorder.EventRecorder; 4 | import eventrecorder.data.LogEntry; 5 | 6 | /** 7 | * Action for changing title and additional information. 8 | * 9 | * @author Andre Muehlenbrock 10 | */ 11 | 12 | public class TitleChangeAction extends Action{ 13 | final String savedTitle; 14 | final String savedAdditional; 15 | final String newTitle; 16 | final String newAdditional; 17 | 18 | public TitleChangeAction(LogEntry entry, String newTitle, String newAdditional, String savedTitle, String savedAdditional) { 19 | super(entry, true); 20 | this.savedTitle = savedTitle; 21 | this.savedAdditional = savedAdditional; 22 | this.newTitle = newTitle; 23 | this.newAdditional = newAdditional; 24 | } 25 | 26 | @Override 27 | public boolean executeAction() { 28 | EventRecorder.model.title = newTitle; 29 | EventRecorder.model.additionalInfo = newAdditional; 30 | 31 | return true; 32 | } 33 | 34 | @Override 35 | public boolean undoAction() { 36 | EventRecorder.model.title = savedTitle; 37 | EventRecorder.model.additionalInfo = savedAdditional; 38 | 39 | return true; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/eventrecorder/data/DataModel.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.data; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * Contains all information of the actual shown records. 7 | * 8 | * @author Andre Muehlenbrock 9 | */ 10 | 11 | public class DataModel { 12 | public String title = ""; 13 | public String additionalInfo = ""; 14 | public final ArrayList logEntries = new ArrayList<>(); 15 | 16 | public int currentTime = 600; 17 | public boolean isManuallyRunning = false; 18 | public long lastGameControllerInfo = 0; 19 | } 20 | -------------------------------------------------------------------------------- /src/eventrecorder/data/LogEntry.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.data; 2 | 3 | /** 4 | * Data of an entry. 5 | * 6 | * @author Andre Muehlenbrock 7 | */ 8 | 9 | public class LogEntry { 10 | public String text; 11 | public String time; 12 | public LogType type; 13 | public boolean firstTimeSet; 14 | 15 | /** 16 | * Creates a new LogEntry object with the given values. 17 | * 18 | * @param text Text 19 | * @param time Time in Seconds 20 | * @param type LogType 21 | */ 22 | 23 | public LogEntry(String text, String time, LogType type){ 24 | this.text = text; 25 | this.time = time; 26 | this.type = type; 27 | this.firstTimeSet = "".equals(time); 28 | } 29 | 30 | /** 31 | * Sets the values of this LogEntry to the value of e. 32 | * 33 | * @param e The LogEntry containing the values. 34 | */ 35 | 36 | public void set(LogEntry e){ 37 | text = e.text; 38 | time = e.time; 39 | type = e.type; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/eventrecorder/data/LogType.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.data; 2 | 3 | /** 4 | * Possible types of a LogEntry. 5 | * 6 | * @author Andre Muehlenbrock 7 | */ 8 | 9 | public enum LogType { 10 | GameState, SetPlayState, PlayerState, Manually 11 | } 12 | -------------------------------------------------------------------------------- /src/eventrecorder/export/MarkDownExporter.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.export; 2 | 3 | import eventrecorder.data.DataModel; 4 | import eventrecorder.data.LogEntry; 5 | import eventrecorder.data.LogType; 6 | 7 | /** 8 | * Exports the given data model to a markdown string. 9 | * 10 | * @author Andre Muehlenbrock 11 | */ 12 | 13 | public class MarkDownExporter { 14 | public static String toMarkDown(DataModel model){ 15 | StringBuilder result = new StringBuilder("## " + model.title + " \n" + model.additionalInfo + " \n\n"); 16 | 17 | for(LogEntry entry : model.logEntries){ 18 | if("".equals(entry.time) && "".equals(entry.text)) 19 | continue; 20 | 21 | if(entry.type == LogType.Manually){ 22 | result.append("- ").append(entry.time).append(": ").append(entry.text).append(" \n"); 23 | } else if(entry.type == LogType.PlayerState || entry.type == LogType.SetPlayState){ 24 | result.append("- *").append(entry.time).append(": ").append(entry.text).append("* \n"); 25 | } else if(entry.type == LogType.GameState){ 26 | result.append("\n**").append(entry.text).append(" (").append(entry.time).append(")** \n\n"); 27 | } 28 | } 29 | 30 | //result += "*Logfile End*"; 31 | 32 | return result.toString(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/eventrecorder/gui/EntryPanel.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.gui; 2 | 3 | import java.awt.BorderLayout; 4 | 5 | import javax.swing.BorderFactory; 6 | import javax.swing.BoxLayout; 7 | import javax.swing.JButton; 8 | import javax.swing.JComboBox; 9 | import javax.swing.JPanel; 10 | 11 | import eventrecorder.EventRecorder; 12 | import eventrecorder.action.EntryDeleteAction; 13 | import eventrecorder.action.EntryTypeChangeAction; 14 | import eventrecorder.data.LogEntry; 15 | import eventrecorder.data.LogType; 16 | 17 | /** 18 | * Displays a line in the logEntryTable. 19 | * 20 | * @author Andre Muehlenbrock 21 | */ 22 | 23 | public class EntryPanel extends JPanel{ 24 | private static final long serialVersionUID = -907777557585101050L; 25 | private final TimeField timeField; 26 | private final TextField textField; 27 | 28 | public EntryPanel(final LogEntry e){ 29 | timeField = new TimeField(e); 30 | textField = new TextField(e); 31 | 32 | setLayout(new BorderLayout()); 33 | setBorder(BorderFactory.createEmptyBorder(3, 3, 0, 3)); 34 | add(timeField, BorderLayout.WEST); 35 | add(textField, BorderLayout.CENTER); 36 | 37 | JPanel controlEntryPanel = new JPanel(); 38 | controlEntryPanel.setLayout(new BoxLayout(controlEntryPanel,BoxLayout.X_AXIS)); 39 | controlEntryPanel.setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); 40 | 41 | 42 | final JComboBox logTypeChooser = new JComboBox<>(new LogType[]{LogType.Manually, LogType.GameState, LogType.PlayerState, LogType.SetPlayState}); 43 | logTypeChooser.setSelectedItem(e.type); 44 | logTypeChooser.addActionListener(aE -> EventRecorder.history.execute(new EntryTypeChangeAction(e, (LogType)logTypeChooser.getSelectedItem(), e.type))); 45 | 46 | JButton deleteButton = new JButton("Delete"); 47 | deleteButton.addActionListener(aE -> EventRecorder.history.execute(new EntryDeleteAction(e))); 48 | 49 | controlEntryPanel.add(logTypeChooser); 50 | controlEntryPanel.add(deleteButton); 51 | 52 | 53 | add(controlEntryPanel, BorderLayout.EAST); 54 | } 55 | 56 | public TimeField getTimeField(){ 57 | return timeField; 58 | } 59 | 60 | public TextField getTextField(){ 61 | return textField; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/eventrecorder/gui/ImageButton.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.gui; 2 | 3 | import java.awt.Image; 4 | import javax.swing.ImageIcon; 5 | import javax.swing.JButton; 6 | 7 | /** 8 | * A JButton with an ImageIcon. 9 | * 10 | * @author Andre Muehlenbrock 11 | */ 12 | public class ImageButton extends JButton{ 13 | private static final long serialVersionUID = -2848062138312840891L; 14 | final ImageIcon enabledIcon; 15 | ImageIcon disabledIcon; 16 | 17 | public ImageButton(String tooltip, String path, int width, int height){ 18 | enabledIcon = new ImageIcon(path); 19 | 20 | enabledIcon.setImage(enabledIcon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH)); 21 | setIcon(enabledIcon); 22 | setToolTipText(tooltip); 23 | setFocusPainted(false); 24 | } 25 | 26 | public ImageButton(String tooltip, String path, String disabledPath, boolean enabled, int width, int height){ 27 | 28 | enabledIcon = new ImageIcon(path); 29 | disabledIcon = new ImageIcon(disabledPath); 30 | 31 | enabledIcon.setImage(enabledIcon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH)); 32 | disabledIcon.setImage(disabledIcon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH)); 33 | 34 | setFocusPainted(false); 35 | 36 | setEnabled(enabled); 37 | 38 | setToolTipText(tooltip); 39 | 40 | } 41 | 42 | @Override 43 | public void setEnabled(boolean b) { 44 | super.setEnabled(b); 45 | 46 | setIcon(!b && disabledIcon != null ? disabledIcon : enabledIcon); 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/eventrecorder/gui/ImageToggleButton.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.gui; 2 | 3 | import java.awt.Image; 4 | 5 | import javax.swing.ImageIcon; 6 | import javax.swing.JToggleButton; 7 | 8 | /** 9 | * A JButton with two ImageIcons which can be switched by clicking. 10 | * 11 | * @author Andre Muehlenbrock 12 | */ 13 | 14 | public class ImageToggleButton extends JToggleButton{ 15 | private static final long serialVersionUID = 1L; 16 | private final ImageIcon enabledIcon; 17 | private ImageIcon disabledIcon; 18 | private boolean isActivated; 19 | 20 | public ImageToggleButton(String tooltip, String path, int width, int height){ 21 | enabledIcon = new ImageIcon(path); 22 | enabledIcon.setImage(enabledIcon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH)); 23 | setIcon(enabledIcon); 24 | setToolTipText(tooltip); 25 | setFocusPainted(false); 26 | } 27 | 28 | public ImageToggleButton(String tooltip, String path, String disabledPath, boolean activated, int width, int height){ 29 | enabledIcon = new ImageIcon(path); 30 | enabledIcon.setImage(enabledIcon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH)); 31 | disabledIcon = new ImageIcon(disabledPath); 32 | disabledIcon.setImage(disabledIcon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH)); 33 | setFocusPainted(false); 34 | 35 | setActivated(activated); 36 | 37 | setToolTipText(tooltip); 38 | 39 | addActionListener(e -> setActivated(!isActivated)); 40 | } 41 | 42 | public void setActivated(boolean b) { 43 | setSelected(b); 44 | setIcon(!b && disabledIcon != null ? disabledIcon : enabledIcon); 45 | isActivated = b; 46 | } 47 | 48 | public boolean isActivated(){ 49 | return isActivated; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/eventrecorder/gui/MenuBar.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.gui; 2 | 3 | import eventrecorder.EventRecorder; 4 | import eventrecorder.export.MarkDownExporter; 5 | import java.awt.BorderLayout; 6 | import java.awt.Dimension; 7 | import java.awt.Toolkit; 8 | 9 | import javax.swing.JCheckBoxMenuItem; 10 | import javax.swing.JFrame; 11 | import javax.swing.JMenu; 12 | import javax.swing.JMenuBar; 13 | import javax.swing.JMenuItem; 14 | import javax.swing.JPanel; 15 | import javax.swing.JScrollPane; 16 | import javax.swing.JTextArea; 17 | 18 | import data.PlayerInfo; 19 | 20 | /** 21 | * The menu bar at the top of the program. 22 | * 23 | * @author Andre Muehlenbrock 24 | */ 25 | public class MenuBar extends JMenuBar { 26 | 27 | private static final long serialVersionUID = -915489966130126852L; 28 | 29 | private final int MARKDOWN_WINDOW_WIDTH = 800; 30 | private final int MARKDOWN_WINDOW_HEIGHT = 600; 31 | 32 | public MenuBar() { 33 | JMenu file = new JMenu("File"); 34 | JMenuItem viewLogFile = new JMenuItem("View as MarkDown"); 35 | viewLogFile.addActionListener(e -> { 36 | JFrame newWindow = new JFrame("Markdown View"); 37 | Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 38 | int width = Math.min((int) screenSize.getWidth(), MARKDOWN_WINDOW_WIDTH); 39 | int height = Math.min((int) screenSize.getHeight(), MARKDOWN_WINDOW_HEIGHT); 40 | int x = ((int) screenSize.getWidth() - width) / 2; 41 | int y = ((int) screenSize.getHeight() - height) / 2; 42 | 43 | // Set some window settings: 44 | newWindow.setBounds(x, y, width, height); 45 | 46 | JPanel main = new JPanel(); 47 | main.setLayout(new BorderLayout()); 48 | 49 | JTextArea textArea = new JTextArea(); 50 | textArea.setText(MarkDownExporter.toMarkDown(EventRecorder.model)); 51 | 52 | main.add(new JScrollPane(textArea), BorderLayout.CENTER); 53 | 54 | newWindow.add(main); 55 | newWindow.setVisible(true); 56 | }); 57 | 58 | JMenuItem saveLogFile = new JMenuItem("Save as MarkDown"); 59 | saveLogFile.addActionListener(e -> EventRecorder.gui.saveAs()); 60 | 61 | JMenuItem exit = new JMenuItem("Exit"); 62 | 63 | file.add(viewLogFile); 64 | file.add(saveLogFile); 65 | file.addSeparator(); 66 | file.add(exit); 67 | 68 | add(file); 69 | 70 | 71 | JMenu logging = new JMenu("Logging"); 72 | 73 | for(int i=0;i<12;i++) { 74 | // Load settings: 75 | EventRecorder.setLogPenalty(i, true); 76 | 77 | String penaltyString = i == 0 ? "Back In Game" : 78 | EventRecorder.capitalize(PlayerInfo.getPenaltyName(i)); 79 | 80 | final int finalI = i; 81 | 82 | // Setup the Checkbox: 83 | final JCheckBoxMenuItem checkBox = new JCheckBoxMenuItem(penaltyString); 84 | checkBox.setSelected(true); 85 | checkBox.addActionListener(e -> EventRecorder.setLogPenalty(finalI, checkBox.isSelected())); 86 | 87 | logging.add(checkBox); 88 | } 89 | 90 | logging.addSeparator(); 91 | 92 | 93 | final JCheckBoxMenuItem checkBox = new JCheckBoxMenuItem("Free Kicks"); 94 | 95 | checkBox.setSelected(true); 96 | checkBox.addActionListener(e -> EventRecorder.setLogFreeKicks(checkBox.isSelected())); 97 | logging.add(checkBox); 98 | 99 | add(logging); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/eventrecorder/gui/TextField.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.gui; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.Font; 5 | import java.awt.Rectangle; 6 | import java.awt.event.FocusEvent; 7 | import java.awt.event.FocusListener; 8 | 9 | import javax.swing.BorderFactory; 10 | import javax.swing.JComponent; 11 | import javax.swing.JScrollPane; 12 | import javax.swing.JTextField; 13 | 14 | import eventrecorder.EventRecorder; 15 | import eventrecorder.action.EntryChangeTextAction; 16 | import eventrecorder.data.LogEntry; 17 | import eventrecorder.data.LogType; 18 | 19 | /** 20 | * This class just display the text of a given LogEntry. 21 | * 22 | * Here is also implemented when a Change-Action is called. 23 | * 24 | * @author Andre Muehlenbrock 25 | */ 26 | 27 | public class TextField extends JTextField{ 28 | private static final long serialVersionUID = -634374539579879231L; 29 | 30 | private final LogEntry entry; 31 | private String savedText = null; 32 | 33 | public TextField(LogEntry entry){ 34 | this.entry = entry; 35 | setText(entry.text); 36 | setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 37 | setPreferredSize(new Dimension(50,getPreferredSize().height)); 38 | setFont(new Font("Monospaced", Font.PLAIN, 14)); 39 | 40 | addFocusListener(new FocusListener(){ 41 | 42 | @Override 43 | public void focusGained(FocusEvent e) { 44 | makeVisibleInScrollPane(); 45 | 46 | savedText = getText(); 47 | } 48 | 49 | @Override 50 | public void focusLost(FocusEvent e) { 51 | executeChangeAction(); 52 | } 53 | }); 54 | 55 | if(entry.type == LogType.Manually){ 56 | setBackground(MainFrame.MANUALLY_LOG_ENTRY_COLOR); 57 | } else if(entry.type == LogType.GameState){ 58 | setBackground(MainFrame.GAMESTATE_LOG_ENTRY_COLOR); 59 | } else if(entry.type == LogType.PlayerState){ 60 | setBackground(MainFrame.PLAYERSTATE_LOG_ENTRY_COLOR); 61 | } 62 | } 63 | 64 | public void executeChangeAction(){ 65 | String newText = getText(); 66 | 67 | if(savedText != null && !savedText.equals(newText)){ 68 | EventRecorder.history.execute(new EntryChangeTextAction(entry, newText, savedText)); 69 | } 70 | 71 | savedText = null; 72 | } 73 | 74 | /** 75 | * This method scrolls the parent scrollpane so that this element is visible. 76 | * 77 | * Ugly code but it works... It is necessary that the parent/child structure is: 78 | * ScrollPane -> (JViewport) -> LogEntryTable -> EntryPanel -> This 79 | */ 80 | 81 | public void makeVisibleInScrollPane(){ 82 | LogEntryTable entryTable = (LogEntryTable) getParent().getParent(); 83 | entryTable.getParent().getParent().getParent().validate(); 84 | entryTable.getParent().getParent().getParent().repaint(); 85 | JComponent parent = (JComponent) entryTable.getParent().getParent(); 86 | JScrollPane scrollPane = (JScrollPane)entryTable.getParent().getParent().getParent(); 87 | float scrollPaneValue = scrollPane.getVerticalScrollBar().getValue(); 88 | 89 | Rectangle b = getParent().getBounds(); 90 | 91 | parent.scrollRectToVisible(new Rectangle(0, (int)(b.y - scrollPaneValue), b.width, b.height)); 92 | } 93 | 94 | public LogEntry getLogEntry(){ 95 | return entry; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/eventrecorder/gui/TimeField.java: -------------------------------------------------------------------------------- 1 | package eventrecorder.gui; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.Font; 5 | import java.awt.Rectangle; 6 | import java.awt.event.FocusEvent; 7 | import java.awt.event.FocusListener; 8 | 9 | import javax.swing.BorderFactory; 10 | import javax.swing.JComponent; 11 | import javax.swing.JScrollPane; 12 | import javax.swing.JTextField; 13 | 14 | import eventrecorder.EventRecorder; 15 | import eventrecorder.action.EntryChangeTimeAction; 16 | import eventrecorder.data.LogEntry; 17 | import eventrecorder.data.LogType; 18 | 19 | /** 20 | * This class just display the time of a given LogEntry. 21 | * 22 | * Here is also implemented when a Change-Action is called. 23 | * 24 | * @author Andre Muehlenbrock 25 | */ 26 | 27 | public class TimeField extends JTextField { 28 | private static final long serialVersionUID = 1446285867560827423L; 29 | private final LogEntry entry; 30 | private String savedTime = null; 31 | 32 | public TimeField(LogEntry entry) { 33 | this.entry = entry; 34 | setText(entry.time); 35 | setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); 36 | setPreferredSize(new Dimension(65,getPreferredSize().height)); 37 | setFont(new Font("Monospaced", Font.PLAIN, 14)); 38 | 39 | addFocusListener(new FocusListener(){ 40 | 41 | @Override 42 | public void focusGained(FocusEvent e) { 43 | makeVisibleInScrollPane(); 44 | 45 | savedTime = getText(); 46 | } 47 | 48 | @Override 49 | public void focusLost(FocusEvent e) { 50 | executeChangeAction(); 51 | } 52 | 53 | }); 54 | 55 | if(entry.type == LogType.Manually){ 56 | setBackground(MainFrame.MANUALLY_LOG_ENTRY_COLOR); 57 | } else if(entry.type == LogType.GameState){ 58 | setBackground(MainFrame.GAMESTATE_LOG_ENTRY_COLOR); 59 | } else if(entry.type == LogType.PlayerState){ 60 | setBackground(MainFrame.PLAYERSTATE_LOG_ENTRY_COLOR); 61 | } 62 | } 63 | 64 | /** 65 | * This method scrolls the parent scrollpane so that this element is visible. 66 | * 67 | * Ugly code but it works... It is necessary that the parent/child structure is: 68 | * ScrollPane -> (JViewport) -> LogEntryTable -> EntryPanel -> This 69 | */ 70 | 71 | public void makeVisibleInScrollPane(){ 72 | LogEntryTable entryTable = (LogEntryTable) getParent().getParent(); 73 | entryTable.getParent().getParent().getParent().validate(); 74 | entryTable.getParent().getParent().getParent().repaint(); 75 | JComponent parent = (JComponent) entryTable.getParent().getParent(); 76 | JScrollPane scrollPane = (JScrollPane)entryTable.getParent().getParent().getParent(); 77 | float scrollPaneValue = scrollPane.getVerticalScrollBar().getValue(); 78 | 79 | Rectangle b = getParent().getBounds(); 80 | 81 | parent.scrollRectToVisible(new Rectangle(0, (int)(b.y - scrollPaneValue), b.width, b.height)); 82 | } 83 | 84 | public void executeChangeAction(){ 85 | String newTime = getText(); 86 | 87 | if(savedTime != null && !savedTime.equals(newTime)) 88 | EventRecorder.history.execute(new EntryChangeTimeAction(entry, newTime, savedTime)); 89 | 90 | savedTime = null; 91 | } 92 | 93 | public LogEntry getLogEntry(){ 94 | return entry; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/exporter/LogExporter.java: -------------------------------------------------------------------------------- 1 | package exporter; 2 | 3 | import common.net.GameControlReturnDataPackage; 4 | import data.GameControlData; 5 | import data.GameControlReturnData; 6 | import data.TeamInfo; 7 | import data.PlayerInfo; 8 | import java.io.ObjectInputStream; 9 | import java.io.IOException; 10 | import java.io.EOFException; 11 | import java.io.BufferedInputStream; 12 | import java.nio.ByteBuffer; 13 | import java.nio.file.Files; 14 | import java.nio.file.Paths; 15 | ///... 16 | 17 | 18 | public class LogExporter 19 | { 20 | /** 21 | * A small exporter that converts TCM log files to a CSV output on the console 22 | * 23 | * @author Tim Laue 24 | */ 25 | public static void main(String[] args) 26 | { 27 | if(args.length == 0) { 28 | System.out.println("No arguments specified!"); 29 | System.out.println("Programs expects the file name of a TCM log file."); 30 | return; 31 | } 32 | String logfile = args[0]; // Name of TCM log to be opened 33 | GameControlData gcd = null; // Pointer to last packet from GameController 34 | char currentGameState = 'X'; // Current game state 35 | 36 | try (ObjectInputStream stream = new ObjectInputStream(new BufferedInputStream(Files.newInputStream(Paths.get(logfile))))) { 37 | // Print header to console: 38 | System.out.println("Timestamp;Game State;Team;Player;Penalized;Pose X;Pose Y"); 39 | while(true) { 40 | final long timestamp = stream.readLong(); 41 | if (stream.readBoolean()) { 42 | final Object obj = stream.readObject(); 43 | if (obj instanceof GameControlReturnDataPackage) { 44 | final GameControlReturnData msg = new GameControlReturnData(); 45 | msg.fromByteArray(ByteBuffer.wrap(((GameControlReturnDataPackage) obj).message)); 46 | // Event is a GameControlReturnData packet -> process message and prepare output 47 | if(gcd != null && currentGameState != 'X') { 48 | int teamNum = msg.teamNum; 49 | int playerNum = msg.playerNum; 50 | // Check, if player is currently penalized: 51 | int team01 = 0; 52 | if(gcd.team[1].teamNumber == teamNum) 53 | team01 = 1; 54 | else if(gcd.team[0].teamNumber != teamNum) { 55 | System.out.println("WTF??!? Strange team number found! Robot has " + teamNum + "!"); 56 | System.out.println("The GameController assumes teams " + gcd.team[0].teamNumber + " and " + gcd.team[1].teamNumber + "."); 57 | return; 58 | } 59 | TeamInfo teamInfo = gcd.team[team01]; 60 | char penaltyChar = teamInfo.player[playerNum-1].penalty != PlayerInfo.PENALTY_NONE ? 'P' : 'U'; 61 | // Finally write data to console: 62 | float poseX = msg.pose[0]; 63 | float poseY = msg.pose[1]; 64 | System.out.println(timestamp + ";" + currentGameState + ";" + teamNum + ";" + playerNum + ";" + penaltyChar + ";" + 65 | poseX + ";" + poseY); 66 | // ADD MORE INTERESTING INFORMATION HERE ... 67 | // ... 68 | // ... 69 | } 70 | } else if (obj instanceof GameControlData) { 71 | // Set game state variable whenever a new packet from the GameController arrives 72 | gcd = (GameControlData)obj; 73 | switch(gcd.gameState) { 74 | case GameControlData.STATE_PLAYING: currentGameState = 'P'; break; 75 | case GameControlData.STATE_READY: currentGameState = 'R'; break; 76 | case GameControlData.STATE_INITIAL: currentGameState = 'I'; break; 77 | case GameControlData.STATE_FINISHED: currentGameState = 'F'; break; 78 | case GameControlData.STATE_SET: currentGameState = 'S'; break; 79 | case GameControlData.STATE_STANDBY: currentGameState = 'Y'; break; 80 | } 81 | } 82 | } /*else if (stream.readInt() == 1) { 83 | current event is a timeout of the GameController connection 84 | nothing to do here now... 85 | }*/ 86 | } 87 | } 88 | catch (EOFException e) { 89 | // Nothing to do here... 90 | // File just ended. 91 | } 92 | catch (IOException | ClassNotFoundException e) { 93 | System.out.println("Exception: " + e); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/exporter/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: exporter.LogExporter 3 | Author: Tim Laue 4 | -------------------------------------------------------------------------------- /src/teamcomm/Config.java: -------------------------------------------------------------------------------- 1 | package teamcomm; 2 | 3 | import common.Log; 4 | import java.io.BufferedInputStream; 5 | import java.io.BufferedOutputStream; 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.io.ObjectInputStream; 9 | import java.io.ObjectOutputStream; 10 | import java.io.Serializable; 11 | import java.nio.file.Files; 12 | import java.nio.file.Paths; 13 | import java.util.HashMap; 14 | 15 | /** 16 | * Singleton class containing arbitrary config data for the TCM. 17 | * 18 | * @author Felix Thielke 19 | */ 20 | public class Config { 21 | 22 | private static final String CONFIG_FILE = "config/TCM.cfg"; 23 | 24 | private static Config instance = null; 25 | 26 | private final HashMap map; 27 | 28 | public static Config getInstance() { 29 | if (instance == null) { 30 | instance = new Config(); 31 | } 32 | return instance; 33 | } 34 | 35 | @SuppressWarnings("unchecked") 36 | private Config() { 37 | HashMap m = new HashMap<>(); 38 | try (final ObjectInputStream stream = new ObjectInputStream(new BufferedInputStream(Files.newInputStream(Paths.get(CONFIG_FILE))))) { 39 | final Object o = stream.readObject(); 40 | if (m.getClass().isInstance(o)) { 41 | m = (HashMap) o; 42 | } 43 | } catch (final IOException | ClassNotFoundException ex) { 44 | } 45 | map = m; 46 | } 47 | 48 | public Object get(final String key) { 49 | return map.get(key); 50 | } 51 | 52 | public void set(final String key, final Serializable config) { 53 | map.put(key, config); 54 | } 55 | 56 | public void flush() { 57 | if (!map.isEmpty()) { 58 | try (final ObjectOutputStream stream = new ObjectOutputStream(new BufferedOutputStream(Files.newOutputStream(Paths.get(CONFIG_FILE))))) { 59 | stream.writeObject(map); 60 | stream.flush(); 61 | } catch (IOException ex) { 62 | Log.error("Could not write TCM config file " + new File(CONFIG_FILE).getAbsolutePath()); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/teamcomm/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: teamcomm.TeamCommunicationMonitor 3 | Author: Felix Thielke 4 | Class-Path: deps/gluegen-rt.jar deps/jogl-all.jar deps/snakeyaml-2.2.jar plugins/common.jar 5 | -------------------------------------------------------------------------------- /src/teamcomm/data/AdvancedMessage.java: -------------------------------------------------------------------------------- 1 | package teamcomm.data; 2 | 3 | import data.SPLTeamMessage; 4 | 5 | /** 6 | * Abstract base class for plugin-based extensions to the SPLTeamMessage. 7 | * 8 | * @author Felix Thielke 9 | */ 10 | public abstract class AdvancedMessage extends SPLTeamMessage { 11 | 12 | private static final long serialVersionUID = 5893551586737053344L; 13 | 14 | public static final String DISPLAY_NEXT_COLUMN = ""; 15 | 16 | /** 17 | * Returns an array of strings containing info from the message which should 18 | * be displayed in the detail window of the sending robots. Each string is 19 | * displayed in a single line. 20 | * 21 | * @return array 22 | */ 23 | public abstract String[] display(); 24 | 25 | /** 26 | * Initializes the message. This method is called once after receiving the 27 | * message and can be used to parse the contents of the custom data array 28 | * into fields. 29 | */ 30 | public abstract void init(); 31 | } 32 | -------------------------------------------------------------------------------- /src/teamcomm/data/event/GameControlDataEvent.java: -------------------------------------------------------------------------------- 1 | package teamcomm.data.event; 2 | 3 | import data.GameControlData; 4 | import java.util.EventObject; 5 | 6 | /** 7 | * Class for events being sent when data from the GameController was received. 8 | * 9 | * @author Felix Thielke 10 | */ 11 | public class GameControlDataEvent extends EventObject { 12 | 13 | private static final long serialVersionUID = -2485519981679815554L; 14 | /** 15 | * The data that was received from the GameController. 16 | */ 17 | public final GameControlData data; 18 | 19 | /** 20 | * Constructor. 21 | * 22 | * @param source source of this event 23 | * @param data 24 | */ 25 | public GameControlDataEvent(final Object source, final GameControlData data) { 26 | super(source); 27 | 28 | this.data = data; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/teamcomm/data/event/GameControlDataEventListener.java: -------------------------------------------------------------------------------- 1 | package teamcomm.data.event; 2 | 3 | import java.util.EventListener; 4 | 5 | /** 6 | * Interface for listeners for events being sent when data was received from the 7 | * GameController. 8 | * 9 | * @author Felix Thielke 10 | */ 11 | public interface GameControlDataEventListener extends EventListener { 12 | 13 | /** 14 | * Called when data was received from the GameController. 15 | * 16 | * @param e event 17 | */ 18 | void gameControlDataChanged(final GameControlDataEvent e); 19 | 20 | /** 21 | * Called when no data was received from the GameController for some time. 22 | * 23 | * @param e event 24 | */ 25 | void gameControlDataTimeout(final GameControlDataTimeoutEvent e); 26 | } 27 | -------------------------------------------------------------------------------- /src/teamcomm/data/event/GameControlDataTimeoutEvent.java: -------------------------------------------------------------------------------- 1 | package teamcomm.data.event; 2 | 3 | import java.util.EventObject; 4 | 5 | /** 6 | * Class for events being sent when no data from the GameController was received 7 | * for some time. 8 | * 9 | * @author Felix Thielke 10 | */ 11 | public class GameControlDataTimeoutEvent extends EventObject { 12 | 13 | private static final long serialVersionUID = 133951243822340672L; 14 | 15 | /** 16 | * Constructor. 17 | * 18 | * @param source source of this event 19 | */ 20 | public GameControlDataTimeoutEvent(final Object source) { 21 | super(source); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/teamcomm/data/event/RobotStateEvent.java: -------------------------------------------------------------------------------- 1 | package teamcomm.data.event; 2 | 3 | import java.util.EventObject; 4 | 5 | /** 6 | * Class for events being sent when the state of a robot changes. 7 | * 8 | * @author Felix Thielke 9 | */ 10 | public class RobotStateEvent extends EventObject { 11 | 12 | private static final long serialVersionUID = 5732929692893474554L; 13 | 14 | /** 15 | * Constructor. 16 | * 17 | * @param source the source of this event 18 | */ 19 | public RobotStateEvent(Object source) { 20 | super(source); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/teamcomm/data/event/RobotStateEventListener.java: -------------------------------------------------------------------------------- 1 | package teamcomm.data.event; 2 | 3 | import java.util.EventListener; 4 | 5 | /** 6 | * Interface for listeners for events being sent when the state of a robot 7 | * changes. 8 | * 9 | * @author Felix Thielke 10 | */ 11 | public interface RobotStateEventListener extends EventListener { 12 | 13 | /** 14 | * Called when the state of the robot which sent this event changes. 15 | * 16 | * @param e event 17 | */ 18 | void robotStateChanged(RobotStateEvent e); 19 | 20 | /** 21 | * Called when the state of the robot which sent this event changes. 22 | * 23 | * @param e event 24 | */ 25 | void connectionStatusChanged(RobotStateEvent e); 26 | } 27 | -------------------------------------------------------------------------------- /src/teamcomm/data/event/TeamEvent.java: -------------------------------------------------------------------------------- 1 | package teamcomm.data.event; 2 | 3 | import java.util.Collection; 4 | import java.util.EventObject; 5 | import teamcomm.data.GameState; 6 | import teamcomm.data.RobotState; 7 | 8 | /** 9 | * Class for events being sent when the state of a team changes. 10 | * 11 | * @author Felix Thielke 12 | */ 13 | public class TeamEvent extends EventObject { 14 | 15 | private static final long serialVersionUID = 6644539300556793797L; 16 | 17 | /** 18 | * Side the team is playing on. 19 | * 20 | * @see GameState#TEAM_LEFT 21 | * @see GameState#TEAM_RIGHT 22 | */ 23 | public final int side; 24 | 25 | /** 26 | * Number of the team. 27 | */ 28 | public final int teamNumber; 29 | 30 | /** 31 | * Robots belonging to the team in the order of their player numbers. 32 | */ 33 | public final Collection players; 34 | 35 | /** 36 | * Constructor. 37 | * 38 | * @param source source of this event 39 | * @param side side the team is playing on 40 | * @param teamNumber number of the team 41 | * @param players robots belonging to the team in the order of their player 42 | * numbers 43 | */ 44 | public TeamEvent(final Object source, final int side, final int teamNumber, final Collection players) { 45 | super(source); 46 | 47 | this.side = side; 48 | this.teamNumber = teamNumber; 49 | this.players = players; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/teamcomm/data/event/TeamEventListener.java: -------------------------------------------------------------------------------- 1 | package teamcomm.data.event; 2 | 3 | import java.util.EventListener; 4 | 5 | /** 6 | * Interface for listeners for events being sent when the state of a team 7 | * changes. 8 | * 9 | * @author Felix Thielke 10 | */ 11 | public interface TeamEventListener extends EventListener { 12 | 13 | /** 14 | * Called when the state of a team changes. 15 | * 16 | * @param e event 17 | */ 18 | void teamChanged(TeamEvent e); 19 | } 20 | -------------------------------------------------------------------------------- /src/teamcomm/gui/Camera.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import com.jogamp.opengl.glu.GLU; 5 | 6 | /** 7 | * Class for modeling the viewer position in a 3D environment. 8 | * 9 | * @author Felix Thielke 10 | */ 11 | public class Camera { 12 | 13 | private static final float NEAR_PLANE = 1; 14 | private static final float FAR_PLANE = 20; 15 | private static final float FOVY = 40; 16 | 17 | private float theta = 45; 18 | private float phi = 0; 19 | private float radius = 9; 20 | private float shift = 0; 21 | private boolean flipped = false; 22 | 23 | /** 24 | * Rotate the camera by 180 degrees around the z axis. 25 | * 26 | * @param gl OpenGL context 27 | */ 28 | public void flip(final GL2 gl) { 29 | flipped = !flipped; 30 | gl.glRotatef(180, 0, 0, 1); 31 | } 32 | 33 | /** 34 | * Adds the given angle to the theta angle (for rotation around the x axis). 35 | * theta is clipped to the range [0,90]. 36 | * 37 | * @param amount angle 38 | */ 39 | public void addTheta(final float amount) { 40 | theta += amount; 41 | if (theta < 0) { 42 | theta = 0; 43 | } else if (theta > 90) { 44 | theta = 90; 45 | } 46 | } 47 | 48 | /** 49 | * Adds the given angle to the phi angle (for rotation around the z axis). 50 | * phi is clipped to the range [-90,90]. 51 | * 52 | * @param amount angle 53 | */ 54 | public void addPhi(final float amount) { 55 | phi += amount; 56 | if (phi < -90) { 57 | phi = -90; 58 | } else if (phi > 90) { 59 | phi = 90; 60 | } 61 | } 62 | 63 | /** 64 | * Adds the given distance to the camera radius. The radius is clipped so 65 | * that the field is always within the viewing frustum. 66 | * 67 | * @param amount distance 68 | */ 69 | public void addRadius(final float amount) { 70 | radius += amount; 71 | if (radius < NEAR_PLANE) { 72 | radius = NEAR_PLANE; 73 | } else if (radius > FAR_PLANE - 5) { 74 | radius = FAR_PLANE - 5; 75 | } 76 | } 77 | 78 | /** 79 | * Sets up the viewing frustum of the camera on the given GL context. To be 80 | * called once whenever the ratio of width and height changes. 81 | * 82 | * @param gl OpenGL context 83 | * @param displayRatio ratio of width to height 84 | */ 85 | public void setupFrustum(final GL2 gl, final double displayRatio) { 86 | final GLU glu = GLU.createGLU(gl); 87 | gl.glMatrixMode(GL2.GL_PROJECTION); 88 | gl.glLoadIdentity(); 89 | glu.gluPerspective(FOVY, displayRatio, NEAR_PLANE, FAR_PLANE); 90 | gl.glMatrixMode(GL2.GL_MODELVIEW); 91 | } 92 | 93 | /** 94 | * Performs transformations on the given OpenGL context which correspond to 95 | * the position of the camera. 96 | * 97 | * @param gl OpenGL context 98 | */ 99 | public void positionCamera(final GL2 gl) { 100 | gl.glTranslatef(0, 0, -radius); 101 | gl.glRotatef(-theta, 1, 0, 0); 102 | gl.glRotatef(phi, 0, 0, 1); 103 | gl.glTranslatef(0, -shift, 0); 104 | } 105 | 106 | /** 107 | * Performs transformations on the given OpenGl context so that the Z axis 108 | * points towards the position of the camera. Useful for rendering text. 109 | * 110 | * @param gl OpenGL context 111 | */ 112 | public void turnTowardsCamera(final GL2 gl) { 113 | gl.glRotatef((flipped ? 180 : 0) - phi, 0, 0, 1); 114 | gl.glRotatef(theta, 1, 0, 0); 115 | } 116 | 117 | /** 118 | * Shifts the camera so that the given Y coordinate on the X-Z-plane is at 119 | * the bottom of the viewport. 120 | * 121 | * @param bottomY Y-coordinate to be shifted to the bottom of the viewport 122 | */ 123 | public void shiftToBottom(final float bottomY) { 124 | shift = bottomY + (float) (Math.sin(Math.PI / 2 - Math.toRadians(theta)) * radius - Math.sin(Math.PI / 2 - (Math.toRadians(theta) + Math.toRadians(FOVY) / 2)) * radius * Math.cos(Math.PI / 2 - Math.toRadians(theta)) / Math.cos(Math.PI / 2 - (Math.toRadians(theta) + Math.toRadians(FOVY) / 2))); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/teamcomm/gui/RobotDetailFrame.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui; 2 | 3 | import java.awt.event.MouseAdapter; 4 | import java.awt.event.MouseEvent; 5 | import javax.swing.JFrame; 6 | import javax.swing.JPanel; 7 | import javax.swing.SwingUtilities; 8 | import teamcomm.data.RobotState; 9 | import teamcomm.data.event.RobotStateEvent; 10 | import teamcomm.data.event.RobotStateEventListener; 11 | 12 | /** 13 | * Class for the windows showing detailed information about robots. 14 | * 15 | * @author Felix Thielke 16 | */ 17 | public abstract class RobotDetailFrame extends JFrame implements RobotStateEventListener { 18 | 19 | private static final long serialVersionUID = 4709653396291218508L; 20 | private final RobotState robot; 21 | 22 | /** 23 | * Constructor. 24 | * 25 | * @param robot robot to create the frame for 26 | * @param anchor panel which triggers the frame on doubleclick 27 | */ 28 | public RobotDetailFrame(final RobotState robot, final JPanel anchor) { 29 | super(robot.getAddress()); 30 | this.robot = robot; 31 | 32 | final RobotStateEventListener listener = this; 33 | 34 | SwingUtilities.invokeLater(() -> { 35 | setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); 36 | anchor.addMouseListener(new MouseAdapter() { 37 | @Override 38 | public void mouseClicked(MouseEvent e) { 39 | if (e.getClickCount() == 2) { 40 | if (!isVisible()) { 41 | setLocationRelativeTo(anchor); 42 | } 43 | setVisible(true); 44 | robotStateChanged(new RobotStateEvent(robot)); 45 | connectionStatusChanged(new RobotStateEvent(robot)); 46 | } 47 | } 48 | }); 49 | 50 | init(robot); 51 | robot.addListener(listener); 52 | }); 53 | } 54 | 55 | /** 56 | * Initialises the frame for the given RobotState. 57 | * 58 | * @param robot RobotState to visualize 59 | */ 60 | protected abstract void init(final RobotState robot); 61 | 62 | /** 63 | * Releases resources of this frame. 64 | */ 65 | public void destroy() { 66 | setVisible(false); 67 | robot.removeListener(this); 68 | } 69 | 70 | /** 71 | * Dispose this frame. 72 | */ 73 | @Override 74 | public void dispose() { 75 | super.dispose(); 76 | robot.removeListener(this); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/Drawing.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import teamcomm.Config; 5 | 6 | /** 7 | * Abstract base class for drawings of the 3D field view. 8 | * 9 | * @author Felix Thielke 10 | */ 11 | public abstract class Drawing { 12 | 13 | private boolean active = true; 14 | 15 | private int teamNumber = -1; 16 | 17 | private boolean initialized = false; 18 | 19 | /** 20 | * Sets whether this drawing is drawn. 21 | * 22 | * @param active boolean 23 | */ 24 | public void setActive(final boolean active) { 25 | this.active = active; 26 | Config.getInstance().set("DrawingActive_" + getClass().getName() + (getTeamNumber() < 0 ? "" : "_" + getTeamNumber()), active); 27 | } 28 | 29 | /** 30 | * Returns whether this drawing is drawn. 31 | * 32 | * @return boolean 33 | */ 34 | public boolean isActive() { 35 | return active; 36 | } 37 | 38 | /** 39 | * Returns the number of the team for which thhis drawing is drawn. 40 | * 41 | * @return team number 42 | */ 43 | public int getTeamNumber() { 44 | return teamNumber; 45 | } 46 | 47 | /** 48 | * Sets the number of the team for which thhis drawing is drawn. 49 | * 50 | * @param teamNumber team number 51 | */ 52 | public void setTeamNumber(final int teamNumber) { 53 | this.teamNumber = teamNumber; 54 | } 55 | 56 | /** 57 | * Handles the initialization of the drawing. 58 | * 59 | * @param gl the OpenGL context this drawing will be drawn on 60 | */ 61 | public final void initialize(final GL2 gl) { 62 | if (!initialized) { 63 | final Boolean confActive = (Boolean) Config.getInstance().get("DrawingActive_" + getClass().getName() + (getTeamNumber() < 0 ? "" : "_" + getTeamNumber())); 64 | 65 | init(gl); 66 | initialized = true; 67 | 68 | if (confActive == null) { 69 | setActive(isActive()); 70 | } else { 71 | setActive(confActive); 72 | } 73 | } 74 | } 75 | 76 | /** 77 | * Performs some initialization, like creating display lists for objects to 78 | * be drawn etc. This method is meant to be overridden by subclasses that 79 | * need initialization. It is guaranteed to be called once before the first 80 | * call of the draw()-method. 81 | * 82 | * @param gl the OpenGL context this drawing will be drawn on 83 | */ 84 | protected void init(final GL2 gl) { 85 | } 86 | 87 | /** 88 | * Indicates whether this drawing contains transparent objects. 89 | * 90 | * @return boolean 91 | */ 92 | public abstract boolean hasAlpha(); 93 | 94 | /** 95 | * The higher the priority, the earlier the drawing is drawn. This only 96 | * matters for transparent drawings so they overlap correctly. Drawings in 97 | * the foreground should have lower priority than drawings in the 98 | * background. 99 | * 100 | * @return priority 101 | */ 102 | public abstract int getPriority(); 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/PerPlayer.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import teamcomm.data.RobotState; 5 | import teamcomm.gui.Camera; 6 | 7 | /** 8 | * Abstract base class for drawings that are drawn for each robot individually. 9 | * 10 | * @author Felix Thielke 11 | */ 12 | public abstract class PerPlayer extends Drawing { 13 | 14 | /** 15 | * Draws this drawing. 16 | * 17 | * @param gl OpenGL context 18 | * @param player robot state of the robot for which this drawing is drawn 19 | * @param camera the camera of the scene 20 | */ 21 | public abstract void draw(final GL2 gl, final RobotState player, final Camera camera); 22 | } 23 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/PerPlayerWithTeam.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import java.util.Collection; 5 | import teamcomm.data.RobotState; 6 | import teamcomm.gui.Camera; 7 | 8 | /** 9 | * Abstract base class for drawings that are drawn for each robot individually, but access data from teammates. 10 | * 11 | * @author Arne Hasselbring 12 | */ 13 | public abstract class PerPlayerWithTeam extends Drawing { 14 | 15 | /** 16 | * Draws this drawing. 17 | * 18 | * @param gl OpenGL context 19 | * @param team robot states of all robots in the team 20 | * @param player robot state of the robot for which this drawing is drawn 21 | * @param camera the camera of the scene 22 | */ 23 | public abstract void draw(final GL2 gl, final Collection team, final RobotState player, final Camera camera); 24 | } 25 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/RoSi2Loader.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import java.io.IOException; 5 | import java.util.Arrays; 6 | import java.util.HashMap; 7 | import java.util.HashSet; 8 | import java.util.Map; 9 | import java.util.Set; 10 | import javax.swing.JOptionPane; 11 | import javax.xml.stream.XMLStreamException; 12 | 13 | /** 14 | * Singleton class for managing the loading of models from ros2 files. 15 | * 16 | * @author Felix Thielke 17 | */ 18 | public class RoSi2Loader { 19 | 20 | /** 21 | * Path to the file containing models for the default drawings. 22 | */ 23 | public static final String COMMON_SCENE_FILE = "scene/TeamComm.ros2"; 24 | 25 | private static final RoSi2Loader instance = new RoSi2Loader(); 26 | private final Map>> models = new HashMap<>(); 27 | private final Map>> modelsToLoad = new HashMap<>(); 28 | 29 | private RoSi2Loader() { 30 | } 31 | 32 | /** 33 | * Returns the only instance of the RoSi2Loader. 34 | * 35 | * @return instance 36 | */ 37 | public static RoSi2Loader getInstance() { 38 | return instance; 39 | } 40 | 41 | /** 42 | * Hints that the given models should be loaded from the common scene file. 43 | * This way the file has to be parsed only once when loading the models. 44 | * 45 | * @param gl OpenGL context 46 | * @param modelnames names of the models to load 47 | */ 48 | public void cacheModels(final GL2 gl, final String[] modelnames) { 49 | cacheModels(gl, COMMON_SCENE_FILE, modelnames); 50 | } 51 | 52 | /** 53 | * Hints that the given models should be loaded from the given scene file. 54 | * This way the file has to be parsed only once when loading the models. 55 | * Calling this method is advised in the init()-method of drawings. 56 | * 57 | * @param gl OpenGL context 58 | * @param filename path to the scene file 59 | * @param modelnames names of the models to load 60 | */ 61 | public void cacheModels(final GL2 gl, final String filename, final String... modelnames) { 62 | final Set modelNameSet = new HashSet<>(Arrays.asList(modelnames)); 63 | 64 | // Check if some of the models have already been loaded 65 | final Map> modelFileMap = models.get(gl); 66 | if (modelFileMap != null) { 67 | final Map modelMap = modelFileMap.get(filename); 68 | if (modelMap != null) { 69 | modelNameSet.removeIf(modelMap::containsKey); 70 | } 71 | } 72 | 73 | // Add models to the set of models to load 74 | if (!modelNameSet.isEmpty()) { 75 | Map> fileMap = modelsToLoad.computeIfAbsent(gl, k -> new HashMap<>()); 76 | Set nameSet = fileMap.get(filename); 77 | if (nameSet == null) { 78 | fileMap.put(filename, modelNameSet); 79 | } else { 80 | nameSet.addAll(modelNameSet); 81 | } 82 | } 83 | } 84 | 85 | /** 86 | * Loads the given model from the common scene file into a display list of 87 | * the given OpenGL context. 88 | * 89 | * @param gl OpenGL context 90 | * @param modelname name of the model 91 | * @return id of the display list 92 | */ 93 | public int loadModel(final GL2 gl, final String modelname) { 94 | return loadModel(gl, COMMON_SCENE_FILE, modelname); 95 | } 96 | 97 | /** 98 | * Loads the given model from the given scene file into a display list of 99 | * the given OpenGL context. 100 | * 101 | * @param gl OpenGL context 102 | * @param filename path to the scene file 103 | * @param modelname name of the model 104 | * @return id of the display list 105 | */ 106 | public int loadModel(final GL2 gl, final String filename, final String modelname) { 107 | // Check if the model has already been loaded 108 | Map> fileMap = models.computeIfAbsent(gl, k -> new HashMap<>()); 109 | Map modelMap = fileMap.get(filename); 110 | if (modelMap == null) { 111 | modelMap = new HashMap<>(); 112 | fileMap.put(filename, modelMap); 113 | } else { 114 | final Integer id = modelMap.get(modelname); 115 | if (id != null) { 116 | return id; 117 | } 118 | } 119 | 120 | // Determine models to load from the same file 121 | Map> loadFileMap = modelsToLoad.computeIfAbsent(gl, k -> new HashMap<>()); 122 | Set nameSet = loadFileMap.computeIfAbsent(filename, k -> new HashSet<>()); 123 | nameSet.add(modelname); 124 | 125 | // Load models 126 | try { 127 | for (final RoSi2Element e : RoSi2Element.parseFile(filename).findElements(nameSet)) { 128 | modelMap.put(e.getName(), e.instantiate(gl).createDisplayList()); 129 | } 130 | } catch (RoSi2Element.RoSi2ParseException | XMLStreamException | IOException ex) { 131 | JOptionPane.showMessageDialog(null, 132 | ex.getMessage(), 133 | "Error loading scene", 134 | JOptionPane.ERROR_MESSAGE); 135 | System.exit(-1); 136 | } 137 | nameSet.clear(); 138 | 139 | return modelMap.get(modelname); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/Static.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import teamcomm.gui.Camera; 5 | 6 | /** 7 | * Abstract base class for drawings that are drawn once. 8 | * 9 | * @author Felix Thielke 10 | */ 11 | public abstract class Static extends Drawing { 12 | 13 | /** 14 | * Draws this drawing. 15 | * 16 | * @param gl OpenGL context 17 | * @param camera the camera of the scene 18 | */ 19 | public abstract void draw(final GL2 gl, final Camera camera); 20 | } 21 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/Text.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings; 2 | 3 | import com.jogamp.opengl.util.awt.TextRenderer; 4 | import java.awt.Font; 5 | import java.awt.geom.Rectangle2D; 6 | 7 | /** 8 | * Helper class for drawing text. 9 | * 10 | * @author Felix Thielke 11 | */ 12 | public class Text { 13 | 14 | private static TextRenderer renderer = null; 15 | 16 | private static final int FONT_SIZE = 72; 17 | 18 | /** 19 | * Draw the given text centered at the given position. 20 | * 21 | * @param text text to draw 22 | * @param centerX center X coordinate at which the text is drawn 23 | * @param centerY center Y coordinate at which the text is drawn 24 | * @param size height of the font 25 | */ 26 | public static void drawText(final String text, final float centerX, final float centerY, final float size) { 27 | drawText(text, centerX, centerY, size, new float[]{1, 1, 1}); 28 | } 29 | 30 | /** 31 | * Draw the given text centered at the given position. 32 | * 33 | * @param text text to draw 34 | * @param centerX center X coordinate at which the text is drawn 35 | * @param centerY center Y coordinate at which the text is drawn 36 | * @param size height of the font 37 | * @param color array with rgb or rgba values describing the color of the 38 | * text (color values are in the range [0.0f,1.0f]) 39 | */ 40 | public static void drawText(final String text, final float centerX, final float centerY, final float size, final float[] color) { 41 | if (renderer == null) { 42 | renderer = new TextRenderer(new Font(Font.DIALOG, Font.BOLD, FONT_SIZE), true, true); 43 | } 44 | 45 | renderer.begin3DRendering(); 46 | renderer.setColor(color[0], color[1], color[2], color.length < 4 ? 1 : color[3]); 47 | final Rectangle2D bounds = renderer.getBounds(text); 48 | renderer.draw3D(text, centerX - (float) (bounds.getWidth() * size / FONT_SIZE) / 2, centerY - (float) (bounds.getHeight() * size / FONT_SIZE) / 2, 0, size / FONT_SIZE); 49 | renderer.end3DRendering(); 50 | } 51 | 52 | /** 53 | * Resets the TextRenderer used for drawing text. This must be called 54 | * whenever the main GL context changes. 55 | */ 56 | public static void resetRenderer() { 57 | renderer = null; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/TextureLoader.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings; 2 | 3 | import com.jogamp.opengl.GL; 4 | import com.jogamp.opengl.GL2; 5 | import java.awt.image.BufferedImage; 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.nio.ByteBuffer; 9 | import java.nio.ByteOrder; 10 | import java.nio.IntBuffer; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import javax.imageio.ImageIO; 14 | 15 | /** 16 | * Singleton class managing the loading of textures from image files. 17 | * 18 | * @author Felix Thielke 19 | */ 20 | public class TextureLoader { 21 | 22 | private static final TextureLoader instance = new TextureLoader(); 23 | private final Map> textures = new HashMap<>(); 24 | 25 | /** 26 | * Class for storing info about a texture. 27 | */ 28 | public static class Texture { 29 | 30 | /** 31 | * OpenGL texture id of the texture. 32 | */ 33 | public final int id; 34 | 35 | /** 36 | * Flag indicating whether the texture has an alpha channel. 37 | */ 38 | public final boolean hasAlpha; 39 | 40 | /** 41 | * Width of the texture in pixels. 42 | */ 43 | public final int width; 44 | 45 | /** 46 | * Height of the texture in pixels. 47 | */ 48 | public final int height; 49 | 50 | /** 51 | * Constructor. 52 | * 53 | * @param id OpenGL texture id of the texture 54 | * @param hasAlpha flag indicating whether the texture has an alpha 55 | * channel 56 | * @param width width of the texture in pixels 57 | * @param height height of the texture in pixels 58 | */ 59 | public Texture(final int id, final boolean hasAlpha, final int width, final int height) { 60 | this.id = id; 61 | this.hasAlpha = hasAlpha; 62 | this.width = width; 63 | this.height = height; 64 | } 65 | 66 | } 67 | 68 | private TextureLoader() { 69 | } 70 | 71 | /** 72 | * Returns the only instance of the TextureLoader. 73 | * 74 | * @return instance 75 | */ 76 | public static TextureLoader getInstance() { 77 | return instance; 78 | } 79 | 80 | /** 81 | * Loads a texture from the given file to the given OpenGL context. 82 | * 83 | * @param gl GL context 84 | * @param filename path of the image file 85 | * @return texture info 86 | * @throws IOException if the file could not be read as an image 87 | */ 88 | public Texture loadTexture(final GL gl, final File filename) throws IOException { 89 | // Get the map for the given gl context 90 | Map map = textures.computeIfAbsent(gl, k -> new HashMap<>()); 91 | 92 | // Check if the texture has already been loaded 93 | Texture tex = map.get(filename.getAbsolutePath()); 94 | if (tex != null) { 95 | return tex; 96 | } 97 | 98 | // Load the image 99 | final BufferedImage img = ImageIO.read(filename); 100 | final int[] imageData = img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth()); 101 | final boolean hasAlpha = img.getColorModel().hasAlpha(); 102 | final ByteBuffer buffer = ByteBuffer.allocate(imageData.length * (hasAlpha ? 4 : 3)); 103 | buffer.order(ByteOrder.LITTLE_ENDIAN); 104 | for (final int c : imageData) { 105 | if (hasAlpha) { 106 | buffer.putInt(c); 107 | } else { 108 | buffer.put((byte) (c & 0xFF)); 109 | buffer.put((byte) ((c >> 8) & 0xFF)); 110 | buffer.put((byte) ((c >> 16) & 0xFF)); 111 | } 112 | } 113 | buffer.rewind(); 114 | 115 | // Allocate texture 116 | final IntBuffer texIds = IntBuffer.allocate(1); 117 | gl.glGenTextures(1, texIds); 118 | final int textureId = texIds.get(0); 119 | 120 | // Load texture into GL 121 | gl.glBindTexture(GL.GL_TEXTURE_2D, textureId); 122 | gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); 123 | gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); 124 | gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, hasAlpha ? GL2.GL_RGBA8 : GL2.GL_RGB8, img.getWidth(), img.getHeight(), 0, hasAlpha ? GL.GL_BGRA : GL2.GL_BGR, GL.GL_UNSIGNED_BYTE, buffer); 125 | gl.glBindTexture(GL.GL_TEXTURE_2D, 0); 126 | tex = new Texture(textureId, hasAlpha, img.getWidth(), img.getHeight()); 127 | map.put(filename.getAbsolutePath(), tex); 128 | 129 | return tex; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/common/Ball.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings.common; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import com.jogamp.opengl.glu.GLU; 5 | import com.jogamp.opengl.glu.GLUquadric; 6 | import data.GameControlReturnData; 7 | import data.PlayerInfo; 8 | import teamcomm.data.RobotState; 9 | import teamcomm.gui.Camera; 10 | import teamcomm.gui.drawings.RoSi2Loader; 11 | import teamcomm.gui.drawings.PerPlayer; 12 | 13 | /** 14 | * Drawing for the ball as seen by a robot. 15 | * 16 | * @author Felix Thielke 17 | */ 18 | public class Ball extends PerPlayer { 19 | 20 | private static final float ROBOT_HEAD_Z = 0.5f; 21 | private static final float BALL_RADIUS = 0.0325f; 22 | private static final float MIN_CYLINDER_RADIUS = 0.01f; 23 | private static final float MAX_BALLAGE = 5.0f; 24 | 25 | @Override 26 | protected void init(GL2 gl) { 27 | RoSi2Loader.getInstance().cacheModels(gl, new String[]{"ball"}); 28 | } 29 | 30 | @Override 31 | public void draw(final GL2 gl, final RobotState player, final Camera camera) { 32 | final GameControlReturnData msg = player.getLastGCRDMessage(); 33 | if (msg != null && msg.poseValid && msg.ballValid && msg.ballAge > -1 && msg.ballAge < MAX_BALLAGE && player.getPenalty() == PlayerInfo.PENALTY_NONE) { 34 | final float[] ball = {msg.ball[0] / 1000.f, msg.ball[1] / 1000.f}; 35 | 36 | gl.glPushMatrix(); 37 | 38 | // Translate to robot 39 | gl.glTranslatef(msg.pose[0] / 1000.f, msg.pose[1] / 1000.f, 0); 40 | gl.glRotatef((float) Math.toDegrees(msg.pose[2]), 0, 0, 1); 41 | 42 | // Translate to ball 43 | gl.glTranslatef(ball[0], ball[1], 0); 44 | 45 | // Draw ball 46 | gl.glCallList(RoSi2Loader.getInstance().loadModel(gl, "ball")); 47 | 48 | // Translate back to robot 49 | gl.glTranslatef(-ball[0], -ball[1], 0); 50 | 51 | // Draw cylinder from robot to ball 52 | gl.glTranslatef(0, 0, ROBOT_HEAD_Z); 53 | gl.glRotatef((float) Math.toDegrees(Math.atan2(ball[1], ball[0])), 0, 0, 1); 54 | gl.glRotatef((float) Math.toDegrees(Math.atan2(Math.sqrt(ball[0] * ball[0] + ball[1] * ball[1]), BALL_RADIUS - ROBOT_HEAD_Z)), 0, 1, 0); 55 | gl.glColorMaterial(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE); 56 | gl.glColor4f(1, 0, 0, 0.25f); 57 | gl.glEnable(GL2.GL_BLEND); 58 | gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA); 59 | final GLU glu = GLU.createGLU(gl); 60 | final GLUquadric q = glu.gluNewQuadric(); 61 | final double cylinderLength = Math.sqrt(ball[0] * ball[0] + ball[1] * ball[1] + (BALL_RADIUS - ROBOT_HEAD_Z) * (BALL_RADIUS - ROBOT_HEAD_Z)); 62 | final double relativeAge = (double) msg.ballAge / (double) MAX_BALLAGE; 63 | final double thinpart = cylinderLength * relativeAge; 64 | final double thickpart = cylinderLength - thinpart; 65 | if (thickpart < 0.05) { 66 | glu.gluCylinder(q, MIN_CYLINDER_RADIUS, MIN_CYLINDER_RADIUS, cylinderLength, 16, 1); 67 | } else { 68 | glu.gluCylinder(q, BALL_RADIUS, BALL_RADIUS, thickpart - 0.05, 16, 1); 69 | gl.glTranslated(0, 0, thickpart - 0.05); 70 | glu.gluCylinder(q, BALL_RADIUS, MIN_CYLINDER_RADIUS, 0.05, 16, 1); 71 | glu.gluCylinder(q, MIN_CYLINDER_RADIUS, MIN_CYLINDER_RADIUS, thinpart, 16, 1); 72 | } 73 | glu.gluDeleteQuadric(q); 74 | gl.glDisable(GL2.GL_BLEND); 75 | 76 | gl.glPopMatrix(); 77 | } 78 | } 79 | 80 | @Override 81 | public boolean hasAlpha() { 82 | return true; 83 | } 84 | 85 | @Override 86 | public int getPriority() { 87 | return 500; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/common/Field.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings.common; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import teamcomm.gui.Camera; 5 | import teamcomm.gui.drawings.RoSi2Loader; 6 | import teamcomm.gui.drawings.Static; 7 | 8 | /** 9 | * Drawing for the field. 10 | * 11 | * @author Felix Thielke 12 | */ 13 | public class Field extends Static { 14 | 15 | @Override 16 | protected void init(GL2 gl) { 17 | RoSi2Loader.getInstance().cacheModels(gl, new String[]{"field"}); 18 | } 19 | 20 | @Override 21 | public void draw(final GL2 gl, final Camera camera) { 22 | gl.glDepthFunc(GL2.GL_LESS); 23 | gl.glCallList(RoSi2Loader.getInstance().loadModel(gl, "field")); 24 | gl.glDepthFunc(GL2.GL_LEQUAL); 25 | } 26 | 27 | @Override 28 | public boolean hasAlpha() { 29 | return true; 30 | } 31 | 32 | @Override 33 | public int getPriority() { 34 | return 1000; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/common/Field2015.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings.common; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import teamcomm.gui.Camera; 5 | import teamcomm.gui.drawings.RoSi2Loader; 6 | import teamcomm.gui.drawings.Static; 7 | 8 | /** 9 | * Drawing for the field. 10 | * 11 | * @author Felix Thielke 12 | */ 13 | public class Field2015 extends Static { 14 | 15 | @Override 16 | protected void init(GL2 gl) { 17 | RoSi2Loader.getInstance().cacheModels(gl, new String[]{"field2015"}); 18 | setActive(false); 19 | } 20 | 21 | @Override 22 | public void draw(final GL2 gl, final Camera camera) { 23 | gl.glDepthFunc(GL2.GL_LESS); 24 | gl.glCallList(RoSi2Loader.getInstance().loadModel(gl, "field2015")); 25 | gl.glDepthFunc(GL2.GL_LEQUAL); 26 | } 27 | 28 | @Override 29 | public boolean hasAlpha() { 30 | return true; 31 | } 32 | 33 | @Override 34 | public int getPriority() { 35 | return 1000; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/common/FieldSmall.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings.common; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import teamcomm.gui.Camera; 5 | import teamcomm.gui.drawings.RoSi2Loader; 6 | import teamcomm.gui.drawings.Static; 7 | 8 | /** 9 | * Drawing for the field. 10 | * 11 | * @author Felix Thielke 12 | */ 13 | public class FieldSmall extends Static { 14 | 15 | @Override 16 | protected void init(GL2 gl) { 17 | RoSi2Loader.getInstance().cacheModels(gl, new String[]{"fieldSmall"}); 18 | setActive(false); 19 | } 20 | 21 | @Override 22 | public void draw(final GL2 gl, final Camera camera) { 23 | gl.glDepthFunc(GL2.GL_LESS); 24 | gl.glCallList(RoSi2Loader.getInstance().loadModel(gl, "fieldSmall")); 25 | gl.glDepthFunc(GL2.GL_LEQUAL); 26 | } 27 | 28 | @Override 29 | public boolean hasAlpha() { 30 | return true; 31 | } 32 | 33 | @Override 34 | public int getPriority() { 35 | return 1000; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/common/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Author: Felix Thielke 3 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/common/Player.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings.common; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import data.GameControlData; 5 | import data.GameControlReturnData; 6 | import data.PlayerInfo; 7 | import data.Rules; 8 | import data.SPL; 9 | import teamcomm.data.GameState; 10 | import teamcomm.data.RobotState; 11 | import teamcomm.gui.Camera; 12 | import teamcomm.gui.drawings.PerPlayer; 13 | import teamcomm.gui.drawings.RoSi2Loader; 14 | 15 | /** 16 | * Drawing for a robot. 17 | * 18 | * @author Felix Thielke 19 | */ 20 | public class Player extends PerPlayer { 21 | 22 | private static String getModelName(final int color) { 23 | switch (color) { 24 | case GameControlData.TEAM_BLUE: 25 | return "robotBlue"; 26 | case GameControlData.TEAM_RED: 27 | return "robotRed"; 28 | case GameControlData.TEAM_BLACK: 29 | return "robotBlack"; 30 | case GameControlData.TEAM_YELLOW: 31 | return "robotYellow"; 32 | case GameControlData.TEAM_WHITE: 33 | return "robotWhite"; 34 | case GameControlData.TEAM_GREEN: 35 | return "robotGreen"; 36 | case GameControlData.TEAM_ORANGE: 37 | return "robotOrange"; 38 | case GameControlData.TEAM_PURPLE: 39 | return "robotPurple"; 40 | case GameControlData.TEAM_BROWN: 41 | return "robotBrown"; 42 | case GameControlData.TEAM_GRAY: 43 | return "robotGray"; 44 | } 45 | 46 | return "robotWhite"; 47 | } 48 | 49 | @Override 50 | protected void init(GL2 gl) { 51 | RoSi2Loader.getInstance().cacheModels(gl, new String[]{"robotBlue", "robotRed", "robotBlack", "robotYellow", "robotWhite", "robotGreen", "robotOrange", "robotPurple", "robotBrown", "robotGray"}); 52 | } 53 | 54 | @Override 55 | public void draw(final GL2 gl, final RobotState player, final Camera camera) { 56 | final GameControlReturnData msg = player.getLastGCRDMessage(); 57 | if (msg != null && msg.poseValid) { 58 | gl.glPushMatrix(); 59 | 60 | if (player.getPenalty() != PlayerInfo.PENALTY_NONE && !(Rules.league instanceof SPL 61 | && (player.getPenalty() == PlayerInfo.PENALTY_SPL_ILLEGAL_MOTION_IN_STANDBY 62 | || player.getPenalty() == PlayerInfo.PENALTY_SPL_ILLEGAL_MOTION_IN_SET))) { 63 | gl.glTranslatef(-msg.playerNum, -3.5f, 0); 64 | gl.glRotatef(-90, 0, 0, 1); 65 | } else { 66 | gl.glTranslatef(msg.pose[0] / 1000.f, msg.pose[1] / 1000.f, 0); 67 | gl.glRotatef((float) Math.toDegrees(msg.pose[2]), 0, 0, 1); 68 | 69 | if (msg.fallenValid && msg.fallen) { 70 | gl.glTranslatef(0, 0, 0.05f); 71 | gl.glRotatef(90, 0, 1, 0); 72 | } 73 | } 74 | 75 | gl.glCallList(RoSi2Loader.getInstance().loadModel(gl, getModelName(GameState.getInstance().getTeamColor(player.getTeamNumber(), player.getPlayerNumber())))); 76 | 77 | gl.glPopMatrix(); 78 | } 79 | } 80 | 81 | @Override 82 | public boolean hasAlpha() { 83 | return false; 84 | } 85 | 86 | @Override 87 | public int getPriority() { 88 | return 0; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/teamcomm/gui/drawings/common/PlayerNumber.java: -------------------------------------------------------------------------------- 1 | package teamcomm.gui.drawings.common; 2 | 3 | import com.jogamp.opengl.GL2; 4 | import data.GameControlReturnData; 5 | import data.PlayerInfo; 6 | import data.Rules; 7 | import data.SPL; 8 | import teamcomm.data.GameState; 9 | import teamcomm.data.RobotState; 10 | import teamcomm.gui.Camera; 11 | import teamcomm.gui.drawings.PerPlayer; 12 | import teamcomm.gui.drawings.Text; 13 | 14 | /** 15 | * Drawing for the player number of a robot. 16 | * 17 | * @author Felix Thielke 18 | */ 19 | public class PlayerNumber extends PerPlayer { 20 | 21 | @Override 22 | public void draw(final GL2 gl, final RobotState player, final Camera camera) { 23 | final GameControlReturnData msg = player.getLastGCRDMessage(); 24 | if (msg != null && msg.playerNumValid && msg.poseValid) { 25 | gl.glPushMatrix(); 26 | 27 | if (player.getPenalty() != PlayerInfo.PENALTY_NONE && !(Rules.league instanceof SPL 28 | && (player.getPenalty() == PlayerInfo.PENALTY_SPL_ILLEGAL_MOTION_IN_STANDBY 29 | || player.getPenalty() == PlayerInfo.PENALTY_SPL_ILLEGAL_MOTION_IN_SET))) { 30 | gl.glTranslatef(-msg.playerNum, -3.5f, 0.7f); 31 | } else { 32 | gl.glTranslatef(msg.pose[0] / 1000.f, msg.pose[1] / 1000.f, 0.7f); 33 | } 34 | 35 | camera.turnTowardsCamera(gl); 36 | Text.drawText("" + msg.playerNum, 0, 0, 0.3f, Rules.league.teamColor[GameState.getInstance().getTeamColor(player.getTeamNumber(), player.getPlayerNumber())].getRGBColorComponents(null)); 37 | 38 | gl.glPopMatrix(); 39 | } 40 | } 41 | 42 | @Override 43 | public boolean hasAlpha() { 44 | return true; 45 | } 46 | 47 | @Override 48 | public int getPriority() { 49 | return 10; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/teamcomm/net/GameControlDataReceiver.java: -------------------------------------------------------------------------------- 1 | package teamcomm.net; 2 | 3 | import common.Log; 4 | import data.GameControlData; 5 | import data.TrueDataRequest; 6 | import java.io.IOException; 7 | import java.net.DatagramPacket; 8 | import java.net.DatagramSocket; 9 | import java.net.InetAddress; 10 | import java.net.InetSocketAddress; 11 | import java.net.SocketException; 12 | import java.net.SocketTimeoutException; 13 | import java.nio.ByteBuffer; 14 | import java.util.EventObject; 15 | import javax.swing.event.EventListenerList; 16 | import teamcomm.data.event.GameControlDataEvent; 17 | import teamcomm.data.event.GameControlDataEventListener; 18 | import teamcomm.data.event.GameControlDataTimeoutEvent; 19 | 20 | /** 21 | * Class for the thread which receives messages from the GameController. 22 | * 23 | * @author Felix Thielke 24 | */ 25 | public class GameControlDataReceiver extends Thread { 26 | 27 | private static final int GAMECONTROLLER_TIMEOUT = 4000; 28 | 29 | private static final int REQUEST_TRUE_DATA_AFTER = 1000; 30 | 31 | private final DatagramSocket datagramSocket; 32 | 33 | private final EventListenerList listeners = new EventListenerList(); 34 | 35 | private final boolean tryToGetTrueData; 36 | 37 | private long timestampOfLastTrueGameControlData = 0; 38 | 39 | /** 40 | * Constructor. 41 | * 42 | * @throws SocketException if the socket cannot be bound 43 | */ 44 | public GameControlDataReceiver() throws SocketException { 45 | this(false); 46 | } 47 | 48 | /** 49 | * Constructor. 50 | * 51 | * @param tryToGetTrueData set to true to request true game state data from 52 | * the GameController 53 | * @throws SocketException if the socket cannot be bound 54 | */ 55 | public GameControlDataReceiver(final boolean tryToGetTrueData) throws SocketException { 56 | setName("GameControlDataReceiver"); 57 | this.tryToGetTrueData = tryToGetTrueData; 58 | 59 | datagramSocket = new DatagramSocket(null); 60 | datagramSocket.setReuseAddress(true); 61 | datagramSocket.setSoTimeout(GAMECONTROLLER_TIMEOUT); 62 | datagramSocket.bind(new InetSocketAddress(GameControlData.GAMECONTROLLER_GAMEDATA_PORT)); 63 | } 64 | 65 | public void addListener(final GameControlDataEventListener listener) { 66 | listeners.add(GameControlDataEventListener.class, listener); 67 | } 68 | 69 | public void removeListener(final GameControlDataEventListener listener) { 70 | listeners.remove(GameControlDataEventListener.class, listener); 71 | } 72 | 73 | private void fireEvent(final EventObject e) { 74 | for (final GameControlDataEventListener listener : listeners.getListeners(GameControlDataEventListener.class)) { 75 | if (e instanceof GameControlDataEvent) { 76 | listener.gameControlDataChanged((GameControlDataEvent) e); 77 | } else if (e instanceof GameControlDataTimeoutEvent) { 78 | listener.gameControlDataTimeout((GameControlDataTimeoutEvent) e); 79 | } 80 | } 81 | } 82 | 83 | /** 84 | * Sends a request for true game state data to the GameController. 85 | * 86 | * @param gameControllerAddress InetAddress of the GameController 87 | * @throws IOException if an error occurred while sending 88 | */ 89 | private void requestTrueData(final InetAddress gameControllerAddress) throws IOException { 90 | final byte[] request = TrueDataRequest.createRequest().toByteArray(); 91 | final DatagramSocket ds = new DatagramSocket(); 92 | ds.send(new DatagramPacket(request, request.length, gameControllerAddress, TrueDataRequest.GAMECONTROLLER_TRUEDATAREQUEST_PORT)); 93 | ds.close(); 94 | } 95 | 96 | @Override 97 | public void run() { 98 | while (!isInterrupted()) { 99 | try { 100 | final ByteBuffer buffer = ByteBuffer.wrap(new byte[GameControlData.SIZE]); 101 | final DatagramPacket packet = new DatagramPacket(buffer.array(), buffer.array().length); 102 | datagramSocket.receive(packet); 103 | 104 | buffer.rewind(); 105 | final GameControlData data = new GameControlData(); 106 | if (data.fromByteArray(buffer)) { 107 | if (tryToGetTrueData == data.isTrueData || (tryToGetTrueData && System.currentTimeMillis() - timestampOfLastTrueGameControlData > 2 * REQUEST_TRUE_DATA_AFTER)) { 108 | fireEvent(new GameControlDataEvent(this, data)); 109 | } 110 | 111 | if (tryToGetTrueData) { 112 | if (data.isTrueData) { 113 | timestampOfLastTrueGameControlData = System.currentTimeMillis(); 114 | } else if (System.currentTimeMillis() - timestampOfLastTrueGameControlData >= REQUEST_TRUE_DATA_AFTER) { 115 | try { 116 | requestTrueData(packet.getAddress()); 117 | } catch (IOException e) { 118 | Log.error("something went wrong trying to request true game data : " + e.getMessage()); 119 | } 120 | } 121 | } 122 | } 123 | } catch (SocketTimeoutException e) { 124 | fireEvent(new GameControlDataTimeoutEvent(this)); 125 | } catch (IOException e) { 126 | Log.error("something went wrong while receiving the game controller packages : " + e.getMessage()); 127 | } 128 | } 129 | 130 | datagramSocket.close(); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/teamcomm/net/GameControlReturnDataReceiverTCM.java: -------------------------------------------------------------------------------- 1 | package teamcomm.net; 2 | 3 | import common.net.GameControlReturnDataPackage; 4 | import common.net.GameControlReturnDataReceiver; 5 | import data.GameControlReturnData; 6 | import java.io.IOException; 7 | import java.nio.ByteBuffer; 8 | import javax.swing.JOptionPane; 9 | import teamcomm.data.GameState; 10 | import teamcomm.net.logging.LogReplayer; 11 | 12 | /** 13 | * Singleton class for the thread which handles GameController return messages from the robots. 14 | * 15 | * @author Felix Thielke 16 | * @author Arne Hasselbring 17 | */ 18 | public class GameControlReturnDataReceiverTCM extends GameControlReturnDataReceiver { 19 | 20 | private static GameControlReturnDataReceiverTCM instance; 21 | 22 | private GameControlReturnDataReceiverTCM() throws IOException { 23 | super(); 24 | } 25 | 26 | /** 27 | * Returns the only instance of the GameControlReturnDataReceiver. 28 | * 29 | * @return instance 30 | */ 31 | public static GameControlReturnDataReceiverTCM getInstance() { 32 | if (instance == null) { 33 | try { 34 | instance = new GameControlReturnDataReceiverTCM(); 35 | } catch (IOException ex) { 36 | JOptionPane.showMessageDialog(null, 37 | "Error while setting up packet listeners: " + ex.getMessage(), 38 | "IOException", 39 | JOptionPane.ERROR_MESSAGE); 40 | System.exit(-1); 41 | } 42 | } 43 | return instance; 44 | } 45 | 46 | @Override 47 | protected boolean processPackets() { 48 | return !LogReplayer.getInstance().isReplaying(); 49 | } 50 | 51 | @Override 52 | protected void handleMessage(final GameControlReturnDataPackage p) { 53 | final GameControlReturnData message = new GameControlReturnData(); 54 | message.fromByteArray(ByteBuffer.wrap(p.message)); 55 | if (!(message.headerValid && message.versionValid && message.playerNumValid && message.teamNumValid)) { 56 | return; 57 | } 58 | 59 | GameState.getInstance().receiveMessage(p.host, message); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/teamcomm/net/SPLTeamMessageReceiverTCM.java: -------------------------------------------------------------------------------- 1 | package teamcomm.net; 2 | 3 | import common.Log; 4 | import common.net.SPLTeamMessagePackage; 5 | import common.net.SPLTeamMessageReceiver; 6 | import data.SPLTeamMessage; 7 | import java.io.IOException; 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.nio.ByteBuffer; 10 | import javax.swing.JOptionPane; 11 | import teamcomm.PluginLoader; 12 | import teamcomm.data.AdvancedMessage; 13 | import teamcomm.data.GameState; 14 | import teamcomm.net.logging.LogReplayer; 15 | 16 | /** 17 | * Singleton class for the thread which handles messages from the robots. It 18 | * spawns one thread for listening on each team port up to team number 100 and 19 | * processes the messages received by these threads. 20 | * 21 | * @author Felix Thielke 22 | */ 23 | public class SPLTeamMessageReceiverTCM extends SPLTeamMessageReceiver { 24 | 25 | private static SPLTeamMessageReceiverTCM instance; 26 | 27 | public SPLTeamMessageReceiverTCM(final boolean multicast) throws IOException { 28 | super(multicast, null); 29 | } 30 | 31 | /** 32 | * Creates the only instance of the SPLTeamMessageReceiver. 33 | * @param multicast Should it also listen to multicast packets? This also means 34 | * that ip adresses are computed based on the player number. 35 | * 36 | * @return instance 37 | */ 38 | public static SPLTeamMessageReceiverTCM createInstance(final boolean multicast) { 39 | try { 40 | instance = new SPLTeamMessageReceiverTCM(multicast); 41 | } catch (IOException ex) { 42 | JOptionPane.showMessageDialog(null, 43 | "Error while setting up packet listeners: " + ex.getMessage(), 44 | "IOException", 45 | JOptionPane.ERROR_MESSAGE); 46 | System.exit(-1); 47 | } 48 | return instance; 49 | } 50 | 51 | /** 52 | * Returns the only instance of the SPLTeamMessageReceiver. 53 | * 54 | * @return instance 55 | */ 56 | public static SPLTeamMessageReceiverTCM getInstance() { 57 | return instance; 58 | } 59 | 60 | @Override 61 | protected boolean processPackets() { 62 | return !LogReplayer.getInstance().isReplaying(); 63 | } 64 | 65 | @Override 66 | protected void handleMessage(final SPLTeamMessagePackage p) { 67 | final SPLTeamMessage message; 68 | final Class c = PluginLoader.getInstance().getMessageClass(p.team); 69 | 70 | try { 71 | message = c.getDeclaredConstructor().newInstance(); 72 | message.fromByteArray(ByteBuffer.wrap(p.message)); 73 | 74 | SPLTeamMessage m = message; 75 | if (message instanceof AdvancedMessage) { 76 | if (message.valid) { 77 | try { 78 | ((AdvancedMessage) message).init(); 79 | } catch (final Throwable e) { 80 | m = SPLTeamMessage.createFrom(message); 81 | Log.error(e.getClass().getSimpleName() + " was thrown while initializing custom message class " + c.getSimpleName() + ": " + e.getMessage()); 82 | } 83 | } else { 84 | m = SPLTeamMessage.createFrom(message); 85 | } 86 | } 87 | 88 | GameState.getInstance().receiveMessage(p.host, p.team, m); 89 | } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) { 90 | Log.error("a problem occured while instantiating custom message class " + c.getSimpleName() + ": " + ex.getMessage()); 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/teamcomm/net/logging/LogReplayEvent.java: -------------------------------------------------------------------------------- 1 | package teamcomm.net.logging; 2 | 3 | import java.util.EventObject; 4 | 5 | /** 6 | * Class for events being sent when the LogReplayTask has updated its state. 7 | * 8 | * @author Felix Thielke 9 | */ 10 | public class LogReplayEvent extends EventObject { 11 | 12 | private static final long serialVersionUID = 8348108129211449571L; 13 | 14 | /** 15 | * Position of the replaying in milliseconds. 16 | */ 17 | public final long timePosition; 18 | 19 | /** 20 | * Whether the replayer is at the beginning of the log file. 21 | */ 22 | public final boolean atBeginning; 23 | 24 | /** 25 | * Whether the replayer is at the end of the log file. 26 | */ 27 | public final boolean atEnd; 28 | 29 | /** 30 | * The current speed of the replayer. 0 means that it is paused. 31 | */ 32 | public final float playbackSpeed; 33 | 34 | /** 35 | * Constructor. 36 | * 37 | * @param source source of this event 38 | * @param timePosition position of the replaying in milliseconds. 39 | * @param atBeginning whether the replayer is at the beginning of the log 40 | * file 41 | * @param atEnd whether the replayer is at the end of the log file 42 | * @param playbackSpeed the current speed of the replayer; 0 means that it 43 | * is paused 44 | */ 45 | public LogReplayEvent(final Object source, final long timePosition, final boolean atBeginning, final boolean atEnd, final float playbackSpeed) { 46 | super(source); 47 | this.timePosition = timePosition; 48 | this.atBeginning = atBeginning; 49 | this.atEnd = atEnd; 50 | this.playbackSpeed = playbackSpeed; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/teamcomm/net/logging/LogReplayEventListener.java: -------------------------------------------------------------------------------- 1 | package teamcomm.net.logging; 2 | 3 | import java.util.EventListener; 4 | 5 | /** 6 | * Interface for listeners for events being sent when the status of replaying a 7 | * log file changes. 8 | * 9 | * @author Felix Thielke 10 | */ 11 | public interface LogReplayEventListener extends EventListener { 12 | 13 | /** 14 | * Called when the status of replaying a log file changes. 15 | * 16 | * @param e event 17 | */ 18 | void logReplayStatus(LogReplayEvent e); 19 | 20 | /** 21 | * Called when a log file was opened. 22 | */ 23 | void logReplayStarted(); 24 | 25 | /** 26 | * Called when a log file was closed. 27 | */ 28 | void logReplayEnded(); 29 | } 30 | -------------------------------------------------------------------------------- /src/teamcomm/net/logging/LogReplayer.java: -------------------------------------------------------------------------------- 1 | package teamcomm.net.logging; 2 | 3 | import common.net.logging.Logger; 4 | import java.io.File; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.ScheduledExecutorService; 9 | import java.util.concurrent.ScheduledFuture; 10 | import java.util.concurrent.TimeUnit; 11 | import javax.swing.event.EventListenerList; 12 | import teamcomm.data.GameState; 13 | import teamcomm.net.GameControlReturnDataReceiverTCM; 14 | import teamcomm.net.SPLTeamMessageReceiverTCM; 15 | 16 | /** 17 | * Singleton class for replaying log files. 18 | * 19 | * @author Felix Thielke 20 | */ 21 | public class LogReplayer { 22 | 23 | private static final LogReplayer instance = new LogReplayer(); 24 | 25 | private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); 26 | private ScheduledFuture taskHandle; 27 | 28 | private LogReplayTask task; 29 | 30 | private final EventListenerList listeners = new EventListenerList(); 31 | 32 | /** 33 | * Returns the only instance of this class. 34 | * 35 | * @return instance 36 | */ 37 | public static LogReplayer getInstance() { 38 | return instance; 39 | } 40 | 41 | /** 42 | * Opens a log file. 43 | * 44 | * @param logfile file 45 | * @throws FileNotFoundException if the file could not be found 46 | * @throws IOException if an other I/O error happened 47 | */ 48 | public void open(final File logfile) throws FileNotFoundException, IOException { 49 | // Close currently opened log 50 | if (task != null && taskHandle != null) { 51 | taskHandle.cancel(false); 52 | task.close(); 53 | for (final LogReplayEventListener listener : listeners.getListeners(LogReplayEventListener.class)) { 54 | listener.logReplayEnded(); 55 | } 56 | } 57 | 58 | // Drain package queue of SPLTeamMessageReceiver and GameControlReturnDataReceiver 59 | SPLTeamMessageReceiverTCM.getInstance().clearPackageQueue(); 60 | GameControlReturnDataReceiverTCM.getInstance().clearPackageQueue(); 61 | 62 | // Reset GameState 63 | GameState.getInstance().reset(); 64 | 65 | // Prevent the logger from logging (disableLogging can't be used for that) 66 | Logger.getInstance().setIsReplaying(true); 67 | 68 | // Open new log 69 | task = new LogReplayTask(logfile, listeners); 70 | taskHandle = scheduler.scheduleAtFixedRate(task, LogReplayTask.PLAYBACK_TASK_DELAY, LogReplayTask.PLAYBACK_TASK_DELAY, TimeUnit.MILLISECONDS); 71 | for (final LogReplayEventListener listener : listeners.getListeners(LogReplayEventListener.class)) { 72 | listener.logReplayStarted(); 73 | } 74 | } 75 | 76 | /** 77 | * Returns whether a log file is currently opened. 78 | * 79 | * @return boolean 80 | */ 81 | public boolean isReplaying() { 82 | return task != null; 83 | } 84 | 85 | /** 86 | * Returns whether the replay of a log file iis currently closed. 87 | * 88 | * @return boolean 89 | */ 90 | public boolean isPaused() { 91 | return task == null || task.isPaused(); 92 | } 93 | 94 | /** 95 | * Sets the speed of the playback. 96 | * 97 | * @param factor playback speed. 1 is normal speed, 0 is paused. 98 | */ 99 | public void setPlaybackSpeed(final float factor) { 100 | if (task != null) { 101 | task.setPlaybackSpeed(factor); 102 | } 103 | } 104 | 105 | /** 106 | * Closes the currently opened log file. 107 | */ 108 | public void close() { 109 | if (task != null) { 110 | // Close currently opened log 111 | taskHandle.cancel(false); 112 | task.close(); 113 | task = null; 114 | taskHandle = null; 115 | 116 | // Send close event 117 | for (final LogReplayEventListener listener : listeners.getListeners(LogReplayEventListener.class)) { 118 | listener.logReplayEnded(); 119 | } 120 | 121 | // Drain package queue of SPLTeamMessageReceiver and GameControlReturnDataReceiver 122 | SPLTeamMessageReceiverTCM.getInstance().clearPackageQueue(); 123 | GameControlReturnDataReceiverTCM.getInstance().clearPackageQueue(); 124 | 125 | // Reset GameState 126 | GameState.getInstance().reset(); 127 | 128 | // Tell the logger that it can log again 129 | Logger.getInstance().setIsReplaying(false); 130 | } 131 | } 132 | 133 | /** 134 | * Adds a listener to receive events about log replaying. 135 | * 136 | * @param listener listener 137 | * @see LogReplayEvent 138 | */ 139 | public void addListener(final LogReplayEventListener listener) { 140 | listeners.add(LogReplayEventListener.class, listener); 141 | } 142 | 143 | /** 144 | * Removes an event listener. 145 | * 146 | * @param listener listener 147 | */ 148 | public void removeListener(final LogReplayEventListener listener) { 149 | listeners.remove(LogReplayEventListener.class, listener); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/tester/GameControllerTester.java: -------------------------------------------------------------------------------- 1 | package tester; 2 | 3 | import common.Log; 4 | import java.net.SocketException; 5 | import teamcomm.net.GameControlDataReceiver; 6 | 7 | /** 8 | * Main class of the GameController tester. 9 | * 10 | * @author Felix Thielke 11 | */ 12 | public class GameControllerTester { 13 | 14 | private static boolean shutdown = false; 15 | private static final Object commandMutex = new Object(); 16 | 17 | /** 18 | * Startup method of the GameController tester. 19 | * 20 | * @param args startup arguments; ignored for now. 21 | */ 22 | public static void main(final String[] args) { 23 | final MainWindow window = new MainWindow(); 24 | GameControlDataReceiver receiver = null; 25 | try { 26 | receiver = new GameControlDataReceiver(); 27 | } catch (SocketException ex) { 28 | Log.error("Could not setup receiver!"); 29 | System.exit(-1); 30 | } 31 | receiver.addListener(window); 32 | 33 | receiver.start(); 34 | 35 | // Wait for shutdown 36 | try { 37 | synchronized (commandMutex) { 38 | while (!shutdown) { 39 | commandMutex.wait(); 40 | } 41 | } 42 | } catch (InterruptedException ex) { 43 | } 44 | 45 | // Shutdown 46 | receiver.interrupt(); 47 | try { 48 | receiver.join(1000); 49 | } catch (InterruptedException ex) { 50 | } 51 | System.exit(0); 52 | } 53 | 54 | /** 55 | * Shuts down the program by notifying the main thread. 56 | */ 57 | public static void shutdown() { 58 | synchronized (commandMutex) { 59 | shutdown = true; 60 | commandMutex.notifyAll(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/tester/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: tester.GameControllerTester 3 | Author: Felix Thielke 4 | -------------------------------------------------------------------------------- /src/tester/MainWindow.java: -------------------------------------------------------------------------------- 1 | package tester; 2 | 3 | import java.awt.Dimension; 4 | import java.awt.event.WindowAdapter; 5 | import java.awt.event.WindowEvent; 6 | import javax.swing.BoxLayout; 7 | import javax.swing.JFrame; 8 | import javax.swing.JLabel; 9 | import javax.swing.JPanel; 10 | import javax.swing.SwingUtilities; 11 | import teamcomm.data.event.GameControlDataEvent; 12 | import teamcomm.data.event.GameControlDataEventListener; 13 | import teamcomm.data.event.GameControlDataTimeoutEvent; 14 | 15 | /** 16 | * Main window of the GameController tester. 17 | * 18 | * @author Felix Thielke 19 | */ 20 | public class MainWindow extends JFrame implements GameControlDataEventListener { 21 | 22 | private static final long serialVersionUID = -2470905865009860120L; 23 | 24 | private static final String DEFAULT_TEXT = "Waiting for messages from the GameController..."; 25 | 26 | final JLabel gcInfo = new JLabel(DEFAULT_TEXT, JLabel.CENTER); 27 | final JLabel team0Info = new JLabel("", JLabel.CENTER); 28 | final JLabel team1Info = new JLabel("", JLabel.CENTER); 29 | 30 | public MainWindow() { 31 | super("GameControllerTester"); 32 | 33 | SwingUtilities.invokeLater(() -> { 34 | setLocationByPlatform(true); 35 | setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 36 | addWindowListener(new WindowAdapter() { 37 | @Override 38 | public void windowClosed(final WindowEvent e) { 39 | GameControllerTester.shutdown(); 40 | } 41 | }); 42 | setPreferredSize(new Dimension(400, 1000)); 43 | 44 | final JPanel contentPane = new JPanel(); 45 | setContentPane(contentPane); 46 | contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.Y_AXIS)); 47 | final JPanel gcInfoContainer = new JPanel(); 48 | gcInfoContainer.setLayout(new BoxLayout(gcInfoContainer, BoxLayout.X_AXIS)); 49 | gcInfoContainer.add(gcInfo); 50 | contentPane.add(gcInfoContainer); 51 | final JPanel teamInfoContainer = new JPanel(); 52 | teamInfoContainer.setLayout(new BoxLayout(teamInfoContainer, BoxLayout.X_AXIS)); 53 | teamInfoContainer.add(team0Info); 54 | teamInfoContainer.add(team1Info); 55 | contentPane.add(teamInfoContainer); 56 | 57 | pack(); 58 | setVisible(true); 59 | }); 60 | } 61 | 62 | @Override 63 | public void gameControlDataChanged(final GameControlDataEvent e) { 64 | gcInfo.setText("" + e.data.toString().replaceAll("\n", "
") + ""); 65 | team0Info.setText("" + e.data.team[0].toString().replaceAll("\n", "
") + ""); 66 | team1Info.setText("" + e.data.team[1].toString().replaceAll("\n", "
") + ""); 67 | } 68 | 69 | @Override 70 | public void gameControlDataTimeout(final GameControlDataTimeoutEvent e) { 71 | gcInfo.setText(DEFAULT_TEXT); 72 | team0Info.setText(""); 73 | team1Info.setText(""); 74 | } 75 | 76 | } 77 | --------------------------------------------------------------------------------