├── .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 extends StreamReader> 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 extends StreamReader> 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 extends StreamReader> readerclass;
17 | private final StreamReader reader;
18 |
19 | public EnumMapReader(final Class enumclass, final Class extends StreamReader> 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 extends StreamReader> 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 extends StreamReader> 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 extends StreamReader>> 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 extends Rules> 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 extends SPLTeamMessage> 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 |
--------------------------------------------------------------------------------