Skip to content

Instantly share code, notes, and snippets.

@donadigo
Created September 18, 2023 10:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save donadigo/c010cd682c9b8eec0dbe3f23dab4188b to your computer and use it in GitHub Desktop.
Save donadigo/c010cd682c9b8eec0dbe3f23dab4188b to your computer and use it in GitHub Desktop.
TMInterface socket example
import socket
import struct
import time
import signal
from tminterface.structs import SimStateData, CheckpointData
HOST = "127.0.0.1"
PORT = 8477
SC_RUN_STEP_SYNC = 0
C_SET_SPEED = 1
C_REWIND_TO_STATE = 2
C_SET_INPUT_STATE = 3
C_SHUTDOWN = 4
sock = None
def signal_handler(sig, frame):
global sock
print('Shutting down...')
sock.sendall(struct.pack('i', C_SHUTDOWN))
sock.close()
def rewind_to_state(sock, state):
sock.sendall(struct.pack('i', C_REWIND_TO_STATE))
sock.sendall(struct.pack('i', len(state.data)))
sock.sendall(state.data)
def set_input_state(sock, up=-1, down=-1, steer=0x7FFFFFFF):
sock.sendall(struct.pack('i', C_SET_INPUT_STATE))
sock.sendall(struct.pack('b', up))
sock.sendall(struct.pack('b', down))
sock.sendall(struct.pack('i', steer))
def respond(sock, type):
sock.sendall(struct.pack('i', type))
def main():
global sock
first_state = 0
ticks_per_second = 0
now = time.time()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
signal.signal(signal.SIGINT, signal_handler)
sock.connect((HOST, PORT))
print('Connected')
while True:
message_type = struct.unpack('i', sock.recv(4))[0]
if message_type == SC_RUN_STEP_SYNC:
state_length = struct.unpack('i', sock.recv(4))[0]
state = SimStateData(sock.recv(state_length))
state.cp_data.resize(CheckpointData.cp_states_field, state.cp_data.cp_states_length)
state.cp_data.resize(CheckpointData.cp_times_field, state.cp_data.cp_times_length)
race_time = state.player_info.race_time
if race_time == 0:
first_state = state
set_input_state(sock, up=True)
if race_time == 3000:
set_input_state(sock, steer=-65536)
if race_time > 0 and race_time % 5000 == 0 and first_state:
rewind_to_state(sock, first_state)
set_input_state(sock, up=True, steer=65536)
respond(sock, SC_RUN_STEP_SYNC)
if time.time() - now > 1:
print(f'Effective speed: {ticks_per_second / 100}x')
now = time.time()
ticks_per_second = 0
ticks_per_second += 1
if __name__ == "__main__":
main()
Net::Socket@ sock = null;
Net::Socket@ clientSock = null;
enum MessageType {
SCRunStepSync = 0,
CSetSpeed,
CRewindToState,
CSetInputState,
CShutdown
}
const string HOST = "127.0.0.1";
const uint16 PORT = 8477;
const uint RESPONSE_TIMEOUT = 2000;
void WaitForResponse(MessageType type)
{
auto now = Time::Now;
while (true) {
auto receivedType = HandleMessage();
if (receivedType == type) {
break;
}
if (receivedType == MessageType::CShutdown) {
break;
}
if (receivedType == -1 && Time::Now - now > RESPONSE_TIMEOUT) {
log("Client disconnected due to timeout (" + RESPONSE_TIMEOUT + "ms)");
@clientSock = null;
break;
}
}
}
int HandleMessage()
{
if (clientSock.Available == 0) {
return -1;
}
auto type = clientSock.ReadInt32();
switch(type) {
case MessageType::SCRunStepSync: {
break;
}
case MessageType::CSetSpeed: {
auto@ simManager = GetSimulationManager();
simManager.SetSpeed(clientSock.ReadFloat());
break;
}
case MessageType::CRewindToState: {
auto stateLength = clientSock.ReadInt32();
auto stateData = clientSock.ReadBytes(stateLength);
auto@ simManager = GetSimulationManager();
if (simManager.InRace) {
SimulationState state(stateData);
simManager.RewindToState(state);
}
break;
}
case MessageType::CSetInputState: {
int8 up = clientSock.ReadInt8();
int8 down = clientSock.ReadInt8();
int steer = clientSock.ReadInt32();
auto@ simManager = GetSimulationManager();
if (simManager.InRace) {
log("Set input state");
simManager.SetInputState(InputType::Up, up);
simManager.SetInputState(InputType::Down, down);
if (steer != 0x7FFFFFFF) {
simManager.SetInputState(InputType::Steer, steer);
}
}
break;
}
case MessageType::CShutdown: {
log("Client disconnected");
@clientSock = null;
break;
}
}
return type;
}
void OnRunStep(SimulationManager@ simManager)
{
if (@clientSock is null) {
return;
}
auto@ state = simManager.SaveState();
auto@ data = state.ToArray();
clientSock.Write(MessageType::SCRunStepSync);
clientSock.Write(data.Length);
clientSock.Write(data);
WaitForResponse(MessageType::SCRunStepSync);
}
void Main()
{
if (@sock is null) {
@sock = Net::Socket();
sock.Listen(HOST, PORT);
}
}
void Render()
{
auto @newSock = sock.Accept(0);
if (@newSock !is null) {
@clientSock = @newSock;
log("Client connected (IP: " + clientSock.RemoteIP + ")");
}
}
PluginInfo@ GetPluginInfo()
{
PluginInfo info;
info.Author = "donadigo";
info.Name = "Test";
info.Description = "Sockets example";
info.Version = "1.0.0";
return info;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment