def client_sync(): global time_since_last_update should_update = time.time() - time_since_last_update > 0.01 if should_update: time_since_last_update = time.time() else: return client_manager = services.client_manager() client = None if client_manager is not None: client = client_manager.get_first_client() if client is None: return else: return while True: pop_element = commandQueue.pop_incoming_command() if not pop_element: # no more entries left in the queue. return False if isinstance(pop_element, ProtocolBufferMessage): omega.send(client.id, pop_element.msg_id, pop_element.msg) elif isinstance(pop_element, HeartbeatMessage): # we don't do anything with Heartbeats. ts4mp_log("latency", time.time() - pop_element.sent_time, force = True) pass
def server_sync(): while True: with Timer("Server Sync"): pop_element = commandQueue.pop_incoming_command() if not pop_element: # no more entries left in the queue. return False # this is a server message coming from the client. Don't parse it. if isinstance(pop_element, ProtocolBufferMessage): continue current_line = pop_element.split(',') function_name = current_line[0] if not function_name: continue parsed_args = list() parse_all_args_except_for_function_name(current_line, function_name, parsed_args) stripped_function_name = function_name.strip() ts4mp_log("arg_handler", stripped_function_name) client_id = replace_client_id_for_ui_commands(stripped_function_name) parsed_args[-1] = client_id ts4mp_log("client_specific", "New function called {} recieved".format(stripped_function_name)) create_and_append_pendable_command(client_id, stripped_function_name) attempt_command(stripped_function_name, parsed_args)
def wrapper_client(func, *args, **kwargs): # Wrapper for functions that have their data needed to be sent to the server. # This is used for client commands so the server can respond. # For example, selecting a choice from the pie menu. # Only supports one multiplayer client at the moment. ts4mp_log("locks", "acquiring outgoing lock") parsed_args = [] for arg in args: if isinstance(arg, RequiredTargetParam): arg = arg.target_id parsed_args.append(arg) ts4mp_log("arg_handler", "\n" + str(func.__name__) + ", " + str(parsed_args) + " " + str(kwargs), force=False) commandQueue.queue_outgoing_command("\n" + str(func.__name__) + ", " + str(parsed_args) + " " + str(kwargs)) def do_nothing(): pass return do_nothing
def _do_command(command_name, *args): if command_name in PERFORM_COMMAND_FUNCTIONS: PERFORM_COMMAND_FUNCTIONS[command_name](*args) ts4mp_log("commands", "There is a command named: {}. Executing it.".format(command_name)) else: ts4mp_log("commands", "There is no such command named: {}!".format(command_name))
def update(self, time_slice=True): ts4mp_log("simulate", "Client is online?: {}".format(execution.client_online), force=False) if execution.client_online: # ts4mp_log("simulate", "Client is online?: {}".format(ts4mp.core.mp_essential.client_online), force=True) return max_time_ms = self.MAX_TIME_SLICE_MILLISECONDS if time_slice else None t1 = time.time() result = self.sim_timeline.simulate(services.game_clock_service().now(), max_time_ms=max_time_ms) t2 = time.time() # ts4mp_log("simulate", "{} ms".format((t2 - t1) * 1000), force=True) if not result: logger.debug( 'Did not finish processing Sim Timeline. Current element: {}', self.sim_timeline.heap[0]) result = self.wall_clock_timeline.simulate( services.server_clock_service().now()) if not result: logger.error( 'Too many iterations processing wall-clock Timeline. Likely culprit: {}', self.wall_clock_timeline.heap[0])
def attempt_command(command, function_name, function_to_execute, incoming_commands, parsed_args): ts4mp_log('arg_handler', str(function_to_execute)) try: _do_command(function_name, *parsed_args) except Exception as e: ts4mp_log("Execution Errors", str(e)) incoming_commands.remove(command)
def _parse_arg(arg): #Horrible, hacky way of parsing arguments from the client commands. #DO NOT EVER CHANGE THESE LINES OF CODE. #IT WILL SCREW UP OBJECT IDS AND VERY LONG NUMBERS, EVEN THOUGH IT SEEMS THAT THIS CODE IS COMPLETELY #USELESS. THE ASSIGNING OF THE VARIABLE TO ANOTHER VARIABLE CAUSES IT TO BREAK IF REMOVED new_arg = arg bool_arg = None if "True" in new_arg: bool_arg = True elif "False" in new_arg: bool_arg = False if bool_arg is not None: return bool_arg orig_arg = new_arg.replace('"', "").replace("(", "").replace(")", "").replace("'", "").strip() new_arg = orig_arg ts4mp_log("arg_handler", "First pass: "******"\n", force=False) try: new_arg = float(orig_arg) try: new_arg = int(orig_arg) except BaseException: pass except BaseException: pass ts4mp_log("arg_handler", "Second pass: "******"\n", force=False) return new_arg
def server_sync(): with incoming_lock: global incoming_commands for command in incoming_commands: # this is a server message coming from the client. Don't parse it. if isinstance(command, ProtocolBufferMessage): continue current_line = command.split(',') function_name = current_line[0] if not function_name: continue parsed_args = list() parse_all_args_except_for_function_name(current_line, function_name, parsed_args) stripped_function_name = function_name.strip() ts4mp_log("arg_handler", stripped_function_name) client_id = replace_client_id_for_ui_commands(stripped_function_name) parsed_args[-1] = client_id function_to_execute = format_command_to_execute(function_name, parsed_args) ts4mp_log("client_specific", "New function called {} recieved".format(stripped_function_name)) create_and_append_pendable_command(client_id, stripped_function_name) attempt_command(command, stripped_function_name, function_to_execute, incoming_commands, parsed_args)
def remove_unneeded_symbols_from_command(arg_index, current_line): arg = current_line[arg_index].replace(')', '').replace('{}', '').replace('(', '').replace("[", "").replace("]", "") ts4mp_log("arg_handler", str(arg) + "\n", force=False) if "'" not in arg and "True" not in arg and "False" not in arg: arg = ALPHABETIC_REGEX.sub('', arg) arg = arg.replace('<._ = ', '').replace('>', '') return arg
def listen_loop(self): serversocket = self.serversocket size = None data = b'' while self.alive: if self.connected: try: new_command, data, size = generic_listen_loop( serversocket, data, size) if new_command is not None: # if we've never received data, then this is the first time we've gotten data. # obviously. if not self.ever_recieved_data: show_client_connect_on_client() ts4mp.core.client_utils.on_successful_client_connect( ) self.ever_recieved_data = True commandQueue.queue_incoming_command(new_command) except socket.error as e: ts4mp_log("sockets", "Catastrophic failure: {}".format(e), force=True) show_client_connection_failure() self.connected = False
def wrapper_client(func, *args, **kwargs): # Wrapper for functions that have their data needed to be sent to the server. # This is used for client commands so the server can respond. # For example, selecting a choice from the pie menu. # Only supports one multiplayer client at the moment. ts4mp_log("locks", "acquiring outgoing lock") with outgoing_lock: # TODO: You should not be referring to a global variable that is in a different module parsed_args = [] for arg in args: if isinstance(arg, RequiredTargetParam): arg = arg.target_id parsed_args.append(arg) ts4mp_log("arg_handler", "\n" + str(func.__name__) + ", " + str(parsed_args) + " " + str(kwargs), force=False) outgoing_commands.append("\n" + str(func.__name__) + ", " + str(parsed_args) + " " + str(kwargs)) # we sleep here so the networking threads can send the commands, if you remove this, it will make the response times # higher time.sleep(0.2) def do_nothing(): pass return do_nothing
def add_event(self, msg_id, msg, immediate=False): if self.client is None: logger.error( 'Could not add event {0} because there are no attached clients', msg_id) return ts4mp_log("client_specific", "Trying to add an event to a client.") function_name = get_command_function_from_pb(msg_id) ts4mp_log("client_specific", "Function is {}".format(function_name)) if function_name is not None: client_id = try_get_client_id_of_pending_command(function_name) ts4mp_log("client_specific", "Client is {}".format(client_id)) if client_id is not None: remove_earliest_command_client(function_name) target_client = self.get_client(client_id) if target_client is not None: target_client.add_event(msg_id, msg, immediate) ts4mp_log("client_specific", "Adding event to client") return ts4mp_log( "client_specific", "No suitable client found, so I'm just going to send it to everybody" ) self.events.append((msg_id, msg)) if immediate: self.process_events()
def get_objects_in_view_gen(_connection=None): all_objs = [] for manager in services.client_object_managers(): for obj in manager.get_all(): #if issubclass(type(obj), BaseSituation): all_objs.append(type(obj)) ts4mp_log("objs in view", str(all_objs))
def get_client(self, client_id): for client_distributor in self.client_distributors: ts4mp_log( "debugging", "Attempting to find client distributor with id: {}, currently found: {}" .format(client_id, client_distributor.client.id)) if client_distributor.client.id == client_id: return client_distributor
def distribute_dialog(self, dialog_type, dialog_msg, immediate=False): distributor_instance = Distributor.instance() distributor_to_send_to = distributor_instance.get_distributor_with_active_sim_matching_sim_id( dialog_msg.owner_id) distributor_instance.add_event_for_client(distributor_to_send_to, dialog_type, dialog_msg, immediate) if dialog_msg: ts4mp_log("events", "{}".format(str(dialog_msg.owner_id)))
def server_sync(): ts4mp_log("locks", "acquiring incoming lock 1") with incoming_lock: global incoming_commands for command in incoming_commands: current_line = command.split(',') function_name = current_line[0] if not function_name: continue parsed_args = list() for arg_index in range(1, len(current_line)): arg = current_line[arg_index].replace(')', '').replace( '{}', '').replace('(', '') if "'" not in arg: arg = ALPHABETIC_REGEX.sub('', arg) arg = arg.replace('<._ = ', '').replace('>', '') parsed_arg = _parse_arg(arg) parsed_args.append(parsed_arg) # set connection to other client client_id = 1000 parsed_args[-1] = client_id function_to_execute = "{}({})".format( function_name, str(parsed_args).replace('[', '').replace(']', '')) function_name = function_name.strip() ts4mp_log("client_specific", "New function called {} recieved".format(function_name)) if function_name in pendable_functions: with pending_commands_lock: if function_name not in pending_commands: pending_commands[function_name] = [] if client_id not in pending_commands[function_name]: pending_commands[function_name].append(client_id) ts4mp_log('arg_handler', str(function_to_execute)) try: _do_command(function_name, *parsed_args) except Exception as e: ts4mp_log("Execution Errors", str(e)) incoming_commands.remove(command) ts4mp_log("locks", "releasing incoming lock")
def show_notif(sim, text): title = "{} said".format( Distributor.instance().get_distributor_with_active_sim_matching_sim_id( sim.id).client._account.persona_name) ts4mp_log("chat", LocalizationHelperTuning.get_raw_text(text)) notification = UiDialogNotification.TunableFactory().default( sim, text=lambda **_: LocalizationHelperTuning.get_raw_text(text), title=lambda **_: LocalizationHelperTuning.get_raw_text(title)) notification.show_dialog(icon_override=(None, sim))
def send_loop(self): while self.alive: if self.clientsocket is not None: ts4mp_log("locks", "acquiring outgoing lock") with outgoing_lock: for data in outgoing_commands: generic_send_loop(data, self.clientsocket) outgoing_commands.remove(data) ts4mp_log("locks", "releasing outgoing lock")
def get_distributor_with_active_sim_matching_sim_id(self, sim_id): for client_distributor in self.client_distributors: if client_distributor.client.active_sim is not None: ts4mp_log( "chat", "Active client sim id: {}".format( client_distributor.client.active_sim.id)) if client_distributor.client.active_sim.id == sim_id: return client_distributor return None
def send_loop(self): self.serversocket.connect((self.host, self.port)) self.connected = True while self.alive: ts4mp_log("locks", "acquiring outgoing lock") with outgoing_lock: for data in outgoing_commands: generic_send_loop(data, self.serversocket) outgoing_commands.remove(data) ts4mp_log("locks", "releasing outgoing lock")
def enter_dialog_callback(dialog): if not dialog.accepted: return dialog_text = dialog.text_input_responses.get("dialog") ts4mp_log("chat", 'Showing message') distributor = Distributor.instance( ).get_distributor_with_active_sim_matching_sim_id(target_id) if distributor is not None: client = distributor.client ts4mp_log("chat", 'Showing message') show_notif(client.active_sim, dialog_text)
def generic_listen_loop(socket, data, size): try: new_command = None if size is None: ts4mp_log("networking state", "Receiving length") size = socket.recv(8) (size, ) = unpack('>Q', size) ts4mp_log("networking state", "Received length is {}".format(size)) size = int(size) elif size > len(data): bytes_to_receive = size - len(data) ts4mp_log("networking state", "Receiving data") new_data = socket.recv(bytes_to_receive) data += new_data elif size == len(data): data = pickle.loads(data) new_command = data size = None data = b'' return new_command, data, size except Exception as e: ts4mp_log("critical error", str(e))
def send_loop(self): while self.alive: messages_sent = 0 if self.clientsocket is not None: ts4mp_log("Messages", "Sending {} messages".format(len(outgoing_commands))) with outgoing_lock: for data in outgoing_commands: generic_send_loop(data, self.clientsocket) outgoing_commands.remove(data) messages_sent += 1 if messages_sent >= 100: break time.sleep(0.2)
def send_lot_architecture_and_reload(_connection=None): output = sims4.commands.CheatOutput(_connection) output("working") zone = services.current_zone() name = str(hex(zone.id)).replace("0x", "") ts4mp_log("zone_id", "{}, {}".format(name, zone.id)) (file_path, file_name) = get_file_matching_name(name) if file_path is not None: with outgoing_lock: ts4mp_log("zone_id", "{}, {}".format(file_path, file_name)) msg = File(name, open(file_path, "rb").read()) outgoing_commands.append(msg)
def listen_loop(self): self.serversocket.listen(5) self.clientsocket, address = self.serversocket.accept() ts4mp_log("network", "Client Connect") clientsocket = self.clientsocket size = None data = b'' while self.alive: new_command, data, size = generic_listen_loop( clientsocket, data, size) if new_command is not None: with incoming_lock: ts4mp.core.mp_essential.incoming_commands.append( new_command)
def get_file_matching_name(name): scratch_directory = "{}saves/scratch".format(get_sims_documents_directory()) file_path = "" file_name = "" for root, _, files in os.walk(scratch_directory): for file_name in files: replaced = file_name.replace("zoneObjects-", "").replace("-6.sav", "").strip() replaced = replaced[1:] ts4mp_log("zone_id", "{} , {}".format(replaced, name)) if name == replaced: file_path = str(os.path.join(root, file_name)) break return (file_path, file_name)
def mp_chat(target_id=None, _connection=None): try: target_id = int(target_id) ts4mp_log("chat", target_id) distributor = Distributor.instance( ).get_distributor_with_active_sim_matching_sim_id(target_id) client = distributor.client ts4mp_log("chat", client) def enter_dialog_callback(dialog): if not dialog.accepted: ts4mp_log("chat", "Dialog was not accepted.") return dialog_text = dialog.text_input_responses.get("dialog") ts4mp_log("chat", 'Showing message') distributor = Distributor.instance( ).get_distributor_with_active_sim_matching_sim_id(target_id) if distributor is not None: client = distributor.client ts4mp_log("chat", 'Showing message') show_notif(client.active_sim, dialog_text) localized_title = lambda **_: LocalizationHelperTuning.get_raw_text( "Say Something") localized_text = lambda **_: LocalizationHelperTuning.get_raw_text( "Say something to anybody who's listening.") localized_fname = lambda **_: LocalizationHelperTuning.get_raw_text( "Type your message here!") localized_lname = lambda **_: LocalizationHelperTuning.get_raw_text( "Type your emote here!") text_input_1 = UiTextInput(sort_order=0) text_input_1.default_text = localized_fname text_input_1.title = None text_input_1.max_length = 100 text_input_1.initial_value = localized_fname text_input_1.length_restriction = Scum_TextInputLengthName() inputs = AttributeDict({'dialog': text_input_1}) dialog = UiDialogTextInputOkCancel.TunableFactory().default( client.active_sim, text=localized_text, title=localized_title, text_inputs=inputs, is_special_dialog=True) ts4mp_log("chat", "Dialog id: {}".format(dialog.dialog_id)) dialog.add_listener(enter_dialog_callback) dialog.show_dialog() except Exception as e: ts4mp_log('chat', e)
def send_loop(self): try: self.serversocket.connect((self.host, self.port)) self.connected = True while self.alive: with outgoing_lock: with Timer("send network time"): for data in outgoing_commands: generic_send_loop(data, self.serversocket) outgoing_commands.remove(data) # time.sleep(1) except Exception as e: show_client_connection_failure() ts4mp_log("sockets", str(e)) self.connected = False
def send_message_server(self, msg_id, msg): # Send message override for the server. # This overrides it so any message for a client with an id of 1000 gets packed into a Message and is placed in the outgoing_commands list for # sending out to the multiplayer clients. # Only supports one multiplayer client at the moment. # TODO: You should not be referring to a global variable that is in a different module if self.id != 1000 and self.active: omega.send(self.id, msg_id, msg.SerializeToString()) # ts4mp_log_debug("msg", msg) else: message = Message(msg_id, msg.SerializeToString()) ts4mp_log("locks", "acquiring outgoing lock") # We use a lock here because outgoing_commands is also being altered by the client socket thread. with outgoing_lock: outgoing_commands.append(message) ts4mp_log("locks", "releasing outgoing lock")
def create_plumbbob(_connection=None): # Gets the current client connection try: sim_instance = services.get_active_sim() sim_position = sim_instance.position new_plumbob_position = sims4.math.Vector3(float(sim_position.x), float(sim_position.y), float(sim_position.z)) plumbob_object = objects.system.create_object(9716920233444718179) plumbob_object.move_to(translation=new_plumbob_position) # output = sims4.commands.CheatOutput(_connection) # output("Spawned plumbob") # SetAsHead.set_head_object(current_sim, created_obj, "0F97B21B") # output("plumb", dir(created_obj)) plumbob_object.set_parent(sim_instance) except Exception as e: ts4mp_log("plumb", e)