11import os
2- from threading import Thread , Event
32from traceback import print_exc
43from typing import Optional
54
@@ -29,11 +28,8 @@ class Bot:
2928 _has_match_settings = False
3029 _has_field_info = False
3130
32- _latest_packet = flat .GameTickPacket ()
31+ _latest_packet : Optional [ flat .GameTickPacket ] = None
3332 _lastest_prediction = flat .BallPrediction ()
34- _packet_event = Event ()
35- _packet_thread = None
36- _run_packet_thread = True
3733
3834 def __init__ (self ):
3935 spawn_id = os .environ .get ("RLBOT_SPAWN_IDS" )
@@ -55,9 +51,6 @@ def __init__(self):
5551 )
5652 self ._game_interface .packet_handlers .append (self ._handle_packet )
5753
58- self ._packet_thread = Thread (target = self ._packet_processor , daemon = True )
59- self ._packet_thread .start ()
60-
6154 self .renderer = Renderer (self ._game_interface )
6255
6356 def _initialize_agent (self ):
@@ -100,9 +93,8 @@ def _handle_ball_prediction(self, ball_prediction: flat.BallPrediction):
10093
10194 def _handle_packet (self , packet : flat .GameTickPacket ):
10295 self ._latest_packet = packet
103- self ._packet_event .set ()
10496
105- def _packet_preprocess (self , packet : flat .GameTickPacket ) -> bool :
97+ def _packet_processor (self , packet : flat .GameTickPacket ):
10698 if (
10799 self .index == - 1
108100 or len (packet .players ) <= self .index
@@ -121,7 +113,7 @@ def _packet_preprocess(self, packet: flat.GameTickPacket) -> bool:
121113 self .logger .error (
122114 "Multiple bots in the game, please set RLBOT_SPAWN_IDS"
123115 )
124- return False
116+ return
125117
126118 player_index = i
127119 self .index = player_index
@@ -132,37 +124,19 @@ def _packet_preprocess(self, packet: flat.GameTickPacket) -> bool:
132124 break
133125
134126 if self .index == - 1 :
135- return False
136-
137- return True
138-
139- def _packet_processor (self ):
140- while self ._run_packet_thread :
141- self ._packet_event .wait ()
142-
143- # if the thread was unblocked,
144- # but it we're not supposed to be running,
145- # then exit
146- if not self ._run_packet_thread :
147127 return
148128
149- self .ball_prediction = self ._lastest_prediction
150- packet : flat .GameTickPacket = self ._latest_packet
129+ self .ball_prediction = self ._lastest_prediction
151130
152- self ._packet_event .clear ()
153-
154- if not self ._packet_preprocess (packet ):
155- continue
156-
157- try :
158- controller = self .get_output (packet )
159- except Exception as e :
160- self .logger .error ("Bot %s returned an error to RLBot: %s" , self .name , e )
161- print_exc ()
162- continue
131+ try :
132+ controller = self .get_output (packet )
133+ except Exception as e :
134+ self .logger .error ("Bot %s returned an error to RLBot: %s" , self .name , e )
135+ print_exc ()
136+ return
163137
164- player_input = flat .PlayerInput (self .index , controller )
165- self ._game_interface .send_player_input (player_input )
138+ player_input = flat .PlayerInput (self .index , controller )
139+ self ._game_interface .send_player_input (player_input )
166140
167141 def run (
168142 self ,
@@ -172,15 +146,43 @@ def run(
172146 rlbot_server_port = int (os .environ .get ("RLBOT_SERVER_PORT" , 23234 ))
173147
174148 try :
175- self ._game_interface .connect_and_run (
149+ self ._game_interface .connect (
176150 wants_match_communcations ,
177151 wants_ball_predictions ,
178152 rlbot_server_port = rlbot_server_port ,
179153 )
180- finally :
181- self ._run_packet_thread = False
182- self ._packet_event .set ()
183154
155+ # custom message handling logic
156+ # this reads all data in the socket until there's no more immediately available
157+ # checks if there was a GameTickPacket in the data, and if so, processes it
158+ # then sets the socket to non-blocking and waits for more data
159+ # if there was no GameTickPacket, it sets to blocking and waits for more data
160+ while True :
161+ try :
162+ self ._game_interface .handle_incoming_messages (True )
163+
164+ # a clean exit means that the socket was closed
165+ break
166+ except BlockingIOError :
167+ # the socket was still open,
168+ # but we don't know if data was read
169+ pass
170+
171+ # check data was read that needs to be processed
172+ if self ._latest_packet is None :
173+ # there's no data we need to process
174+ # data is coming, but we haven't gotten it yet - wait for it
175+ # after `handle_incoming_messages` gets it's first message,
176+ # it will set the socket back to non-blocking on its own
177+ # that will ensure that `BlockingIOError` gets raised
178+ # when it's done reading the next batch of messages
179+ self ._game_interface .socket .setblocking (True )
180+ continue
181+
182+ # process the packet that we got
183+ self ._packet_processor (self ._latest_packet )
184+ self ._latest_packet = None
185+ finally :
184186 self .retire ()
185187 del self ._game_interface
186188
0 commit comments