Skip to content

Commit 969eb52

Browse files
authored
Merge branch 'main' into do_preprocess
2 parents 069e0b3 + 7050a6a commit 969eb52

File tree

8 files changed

+287
-12
lines changed

8 files changed

+287
-12
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.venv/
2+
scripts/proxy
3+
scripts/rcssserver

check_requirements.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
import pkg_resources
3+
import sys
4+
5+
def check_requirements(requirements_file='requirements.txt'):
6+
with open(requirements_file, 'r') as file:
7+
requirements = file.readlines()
8+
9+
for requirement in requirements:
10+
requirement = requirement.strip()
11+
try:
12+
pkg_resources.require(requirement)
13+
except pkg_resources.VersionConflict as e:
14+
print(f"WARNING: {str(e)}")
15+
except pkg_resources.DistributionNotFound as e:
16+
print(f"ERROR: {str(e)}")
17+
sys.exit(1)
18+
19+
if __name__ == "__main__":
20+
check_requirements()

scripts/download-proxy.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/sh
2+
3+
# check proxy directory exists, if exists, remove it
4+
if [ -d proxy ]; then
5+
echo "proxy directory exists, remove it"
6+
rm -rf proxy
7+
fi
8+
9+
mkdir proxy
10+
11+
cd proxy
12+
13+
# Check if curl exists
14+
if command -v curl >/dev/null 2>&1; then
15+
echo "curl is installed."
16+
else
17+
echo "curl is not installed. Please install it."
18+
exit 1
19+
fi
20+
21+
# Check if get exists
22+
if command -v wget >/dev/null 2>&1; then
23+
echo "wget is installed."
24+
else
25+
echo "wget is not installed. Please install it."
26+
exit 1
27+
fi
28+
29+
# download soccer simulation proxy
30+
wget $(curl -s "https://api.github.com/repos/clsframework/soccer-simulation-proxy/releases/latest" | grep -oP '"browser_download_url": "\K[^"]*' | grep "soccer-simulation-proxy.tar.gz")
31+
32+
tar -xvf soccer-simulation-proxy.tar.gz
33+
34+
mv soccer-simulation-proxy/* .
35+
36+
rm -rf soccer-simulation-proxy
37+
38+
rm soccer-simulation-proxy.tar.gz

scripts/download-rcssserver.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/bin/sh
2+
3+
# check rcssserver directory exists, if exists, remove it
4+
if [ -d rcssserver ]; then
5+
echo "rcssserver directory exists, remove it"
6+
rm -rf rcssserver
7+
fi
8+
9+
mkdir rcssserver
10+
11+
cd rcssserver
12+
13+
# Check if curl exists
14+
if command -v curl >/dev/null 2>&1; then
15+
echo "curl is installed."
16+
else
17+
echo "curl is not installed. Please install it."
18+
exit 1
19+
fi
20+
21+
# Check if get exists
22+
if command -v wget >/dev/null 2>&1; then
23+
echo "wget is installed."
24+
else
25+
echo "wget is not installed. Please install it."
26+
exit 1
27+
fi
28+
29+
# download soccer simulation server App Image
30+
wget $(curl -s https://api.github.com/repos/clsframework/rcssserver/releases/latest | grep -oP '"browser_download_url": "\K(.*rcssserver-x86_64-.*\.AppImage)' | head -n 1)
31+
32+
# check download is successful
33+
if [ ! -f *.AppImage ]; then
34+
echo "Download failed"
35+
exit 1
36+
fi
37+
38+
mv rcssserver-x86_64-*.AppImage rcssserver
39+
40+
chmod +x rcssserver

server.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@
66
from multiprocessing import Manager, Lock
77
import logging
88
import grpc
9+
import argparse
10+
911

1012
logging.basicConfig(level=logging.DEBUG)
1113

12-
manager = Manager()
13-
shared_lock = Lock() # Create a Lock for synchronization
14-
shared_number_of_connections = manager.Value('i', 0)
1514

1615
class GrpcAgent:
1716
def __init__(self, agent_type, uniform_number) -> None:
@@ -75,8 +74,10 @@ def GetTrainerActions(self, state: pb2.State):
7574
return pb2.TrainerActions(actions=actions)
7675

7776
class GameHandler(pb2_grpc.GameServicer):
78-
def __init__(self):
77+
def __init__(self, shared_lock, shared_number_of_connections) -> None:
7978
self.agents: dict[int, GrpcAgent] = {}
79+
self.shared_lock = shared_lock
80+
self.shared_number_of_connections = shared_number_of_connections
8081

8182
def GetPlayerActions(self, state: pb2.State, context):
8283
logging.debug(f"GetPlayerActions unum {state.register_response.uniform_number} at {state.world_model.cycle}")
@@ -122,14 +123,14 @@ def Register(self, register_request: pb2.RegisterRequest, context):
122123
logging.debug(f"received register request from team_name: {register_request.team_name} "
123124
f"unum: {register_request.uniform_number} "
124125
f"agent_type: {register_request.agent_type}")
125-
with shared_lock:
126-
shared_number_of_connections.value += 1
127-
logging.debug(f"Number of connections {shared_number_of_connections.value}")
126+
with self.shared_lock:
127+
self.shared_number_of_connections.value += 1
128+
logging.debug(f"Number of connections {self.shared_number_of_connections.value}")
128129
team_name = register_request.team_name
129130
uniform_number = register_request.uniform_number
130131
agent_type = register_request.agent_type
131-
self.agents[shared_number_of_connections.value] = GrpcAgent(agent_type, uniform_number)
132-
res = pb2.RegisterResponse(client_id=shared_number_of_connections.value,
132+
self.agents[self.shared_number_of_connections.value] = GrpcAgent(agent_type, uniform_number)
133+
res = pb2.RegisterResponse(client_id=self.shared_number_of_connections.value,
133134
team_name=team_name,
134135
uniform_number=uniform_number,
135136
agent_type=agent_type)
@@ -143,9 +144,9 @@ def SendByeCommand(self, register_response: pb2.RegisterResponse, context):
143144
res = pb2.Empty()
144145
return res
145146

146-
def serve(port):
147+
def serve(port, shared_lock, shared_number_of_connections):
147148
server = grpc.server(futures.ThreadPoolExecutor(max_workers=22))
148-
game_service = GameHandler()
149+
game_service = GameHandler(shared_lock, shared_number_of_connections)
149150
pb2_grpc.add_GameServicer_to_server(game_service, server)
150151
server.add_insecure_port(f'[::]:{port}')
151152
server.start()
@@ -154,5 +155,15 @@ def serve(port):
154155
server.wait_for_termination()
155156

156157

158+
def main():
159+
manager = Manager()
160+
shared_lock = Lock() # Create a Lock for synchronization
161+
shared_number_of_connections = manager.Value('i', 0)
162+
parser = argparse.ArgumentParser(description='Run play maker server')
163+
parser.add_argument('-p', '--rpc-port', required=False, help='The port of the server', default=50051)
164+
args = parser.parse_args()
165+
serve(args.rpc_port, shared_lock, shared_number_of_connections)
166+
157167
if __name__ == '__main__':
158-
serve(50051)
168+
main()
169+

service_pb2.py

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

start-team.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import subprocess
2+
import os
3+
import signal
4+
import threading
5+
import logging
6+
import argparse
7+
import check_requirements
8+
9+
10+
# Set up logging
11+
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
12+
13+
def run_server_script(args):
14+
# Start the server.py script as a new process group
15+
process = subprocess.Popen(
16+
['python3', 'server.py', '--rpc-port', args.rpc_port],
17+
preexec_fn=os.setsid, # Create a new session and set the process group ID
18+
stdout=subprocess.PIPE,
19+
stderr=subprocess.STDOUT # Capture stderr and redirect it to stdout
20+
)
21+
return process
22+
23+
def run_start_script(args):
24+
# Start the start.sh script in its own directory as a new process group
25+
process = subprocess.Popen(
26+
['bash', 'start.sh', '-t', args.team_name, '--rpc-port', args.rpc_port, '--rpc-type', 'grpc'],
27+
cwd='scripts/proxy', # Corrected directory to where start.sh is located
28+
preexec_fn=os.setsid, # Create a new session and set the process group ID
29+
stdout=subprocess.PIPE,
30+
stderr=subprocess.STDOUT # Capture stderr and redirect it to stdout
31+
)
32+
return process
33+
34+
def stream_output(process, prefix):
35+
# Stream output from the process and log it with a prefix
36+
for line in iter(process.stdout.readline, b''):
37+
logging.debug(f'{prefix} {line.decode().strip()}')
38+
process.stdout.close()
39+
40+
def kill_process_group(process):
41+
try:
42+
os.killpg(os.getpgid(process.pid), signal.SIGTERM) # Send SIGTERM to the process group
43+
except ProcessLookupError:
44+
pass # The process might have already exited
45+
46+
if __name__ == "__main__":
47+
# Set up argument parsing
48+
parser = argparse.ArgumentParser(description='Run server and team scripts.')
49+
parser.add_argument('-t', '--team_name', required=False, help='The name of the team', default='CLS')
50+
parser.add_argument('--rpc-port', required=False, help='The port of the server', default='50051')
51+
args = parser.parse_args()
52+
53+
try:
54+
# Check Python requirements
55+
logging.debug("Checking Python requirements...")
56+
check_requirements.check_requirements()
57+
58+
# Run the server.py script first
59+
server_process = run_server_script(args)
60+
logging.debug(f"Started server.py process with PID: {server_process.pid}")
61+
62+
# Run the start.sh script after server.py with the given arguments
63+
start_process = run_start_script(args)
64+
logging.debug(f"Started start.sh process with PID: {start_process.pid} with team name {args=}")
65+
66+
# Monitor both processes and log their outputs
67+
server_thread = threading.Thread(target=stream_output, args=(server_process, 'server:'))
68+
start_thread = threading.Thread(target=stream_output, args=(start_process, 'team:'))
69+
70+
server_thread.start()
71+
start_thread.start()
72+
73+
# Wait for both threads to finish
74+
server_thread.join()
75+
start_thread.join()
76+
77+
except KeyboardInterrupt:
78+
logging.debug("Interrupted! Killing all processes.")
79+
kill_process_group(server_process)
80+
kill_process_group(start_process)
81+
82+
finally:
83+
# Ensure all processes are killed on exit
84+
kill_process_group(server_process)
85+
kill_process_group(start_process)

start-team.sh

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/bin/bash
2+
3+
# Ensure the script exits if any command fails
4+
set -e
5+
# check scripts/proxy directory does not exist, raise error
6+
if [! -d scripts/proxy ]; then
7+
echo "scripts/proxy directory does not exist"
8+
exit 1
9+
fi
10+
11+
team_name="CLS"
12+
rpc_port=50051
13+
14+
# help function
15+
usage() {
16+
echo "Usage: $0 [options]"
17+
echo "Options:"
18+
echo " -t team_name: specify team name"
19+
echo " --rpc-port PORT - specifies rpc port (default: 50051)"
20+
exit 1
21+
}
22+
23+
while [ $# -gt 0 ]
24+
do
25+
case $1 in
26+
-t)
27+
team_name=$2
28+
shift
29+
;;
30+
--rpc-port)
31+
rpc_port=$2
32+
shift
33+
;;
34+
*)
35+
echo 1>&2
36+
echo "invalid option \"${1}\"." 1>&2
37+
echo 1>&2
38+
usage
39+
exit 1
40+
;;
41+
esac
42+
43+
shift 1
44+
done
45+
46+
# Check Python requirements
47+
echo "Checking Python requirements..."
48+
python3 check_requirements.py
49+
50+
# Start server.py in the background
51+
echo "Starting server.py..."
52+
python3 server.py --rpc-port $rpc_port &
53+
server_pid=$!
54+
55+
# Function to kill server and team processes on exit
56+
cleanup() {
57+
echo "Cleaning up..."
58+
kill $server_pid
59+
kill $start_pid
60+
}
61+
62+
# Trap the exit signal to cleanup processes
63+
trap cleanup EXIT
64+
65+
# Wait a moment to ensure the server has started (optional)
66+
sleep 2
67+
68+
# Start start.sh script in the correct directory with arguments
69+
echo "Starting start.sh with team name: $team_name and ..."
70+
cd scripts/proxy
71+
bash start.sh -t "$team_name" --rpc-port $rpc_port --rpc-type grpc &
72+
start_pid=$!
73+
74+
# Wait for both background processes to finish
75+
wait $server_pid
76+
wait $start_pid

0 commit comments

Comments
 (0)