class MultiplayerServerClass(NetworkPacketClass, CardDeckClass): def __init__(self, ip_port, game_rules): self.server_data = dict.copy(self.get_network_packet_definition()) self.setup_game_rules_to_server_data(game_rules) self._dealer = DealerClass(data=self.server_data) self.mutex = QMutex() self.conn_player_number = 0 self.conn_players_addresses = [0 for x in range(MAX_CLIENTS)] self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: self.s.bind(ip_port) except socket.error as e: str(e) self.s.listen(MAX_CLIENTS) print("Server Started... Waiting for a connection") thread = threading.Thread(target=self.server_listening_for_new_connections_loop, args=()) thread.start() def server_listening_for_new_connections_loop(self): while True: conn, addr = self.s.accept() self.conn_players_addresses[self.conn_player_number] = addr # save the connected player info print(f'SERVER: New Connection Established. Client (C{self.conn_player_number}) from {self.conn_players_addresses[CLIENT_SRV]}.') thread = threading.Thread(target=self.main_communication_loop, args=(conn, self.conn_player_number)) thread.start() self.conn_player_number += 1 print(f'SERVER: Active connections: {self.conn_player_number}. ') def main_communication_loop(self, conn, client_number): # Handshake message send_simple_message(conn, client_number) connected = True while connected: try: # We send 2 messages between client-server. # First we find the size of the dictionary we want to send, and tell the server to receive that size. # Second we send the dictionary in string format. client_data = receive_message_with_size_confirmation(conn) client_data = transform_into_data(client_data) while True: # Try to access Server Data (Thread-Safe) if self.mutex.tryLock(): # Are we still connected to this player? if client_data: self.server_data[client_number][PS_ConnectionStatus] = CONN_STATUS_CONNECTED else: self.server_data[client_number][PS_ConnectionStatus] = CONN_STATUS_DISCONNECTED # Copy the clients data onto the server data. self.server_update_from_this_player_data_fields(client_data=client_data, client_id=client_number) if client_number == CLIENT_SRV: # The server should theoretically loop through communication with all clients # So if we do the dealer logic only on player0 (DEALER) then we should theoretically # do the dealer logic only once per update to all other clients. self._dealer.dealer_evaluate_next_step() # Send the updated info back to the client: server_reply = transform_into_string(self.server_data) # Unlock the server_data_dict for other threads. self.mutex.unlock() break if connected: send_message_with_size_confirmation(conn, server_reply) print(f'SERVER: Communication done for C{client_number}.') except socket.error as e: print(f'SERVER: main_communication_loop -> {e}') send_message_with_size_confirmation(conn, MESSAGE_DISCONNECTED) self.conn_player_number -= 1 conn.close() break def setup_game_rules_to_server_data(self, rules): # 0 gameName = self._win.getGameName() # 1 startingMoney = self._win.getStartingAmount() # 2 currency = self._win.getCurrency() # 3 bigBlind = self._win.getBigBlind() # 4 blindInterval = self._win.getBlindInterval() # tpl = (gameName, startingMoney, currency, bigBlind, blindInterval) for player in range(MAX_CLIENTS): self.server_data[player][PS_MoneyAvailable] = rules[1] self.server_data[player][PS_MoneyBoughtIn] = rules[1] self.server_data[DL_GameName] = str(rules[0]) self.server_data[DL_Currency] = str(rules[2]) self.server_data[DL_BigBlind] = float(rules[3]) def server_update_from_this_player_data_fields(self, client_data, client_id): self.server_data[client_id][PC_Name] = client_data[client_id][PC_Name] self.server_data[client_id][PC_Icon] = client_data[client_id][PC_Icon] self.server_data[client_id][PC_TableSpot] = client_data[client_id][PC_TableSpot] self.server_data[client_id][PC_BuyInReq] = client_data[client_id][PC_BuyInReq] self.server_data[client_id][PC_idPlayerAction] = client_data[client_id][PC_idPlayerAction] self.server_data[client_id][PC_isPlayerPlaying] = client_data[client_id][PC_isPlayerPlaying] self.server_data[client_id][PC_BetAmount] = client_data[client_id][PC_BetAmount] self.server_data[client_id][PC_ClientOverwrite] = client_data[client_id][PC_ClientOverwrite]
class RocketSimulator(QtCore.QObject): R_EARTH = 6371000 # meters G_0 = -9.80665 # m/s^2 (at sea level) new_data = QtCore.pyqtSignal(object) def __init__(self, ticksize, param_file='parameters.json'): QtCore.QObject.__init__(self) self.load_data(param_file) def load_data(self, param_file): with open(param_file, 'r') as inputf: self.parameters = json.load(inputf) # Set imported parameters as properties for parameter in self.parameters: setattr(self, parameter, self.parameters[parameter]) # use for threadsafe commnications with the GUI thread self.mutex = QMutex() self.atmosphere = Atmosphere() # TODO Error analysis vs. ticksize self.ticksize = 0.001 self.time = 0 self.height = self.launch_height self.velocity = 0 self.acceleration = 0 self.max_height = 0 self.max_velocity = 0 self.max_acceleration = 0 self.mass = self.launch_mass self.thruster = Thruster() self.data = {} self.data['time'] = [] self.data['height'] = [] self.data['velocity'] = [] self.data['acceleration'] = [] def run_simulation(self): while self.height >= self.launch_height: self.run_tick() print(self.max_height, self.max_velocity, self.max_acceleration) def run_tick(self): self.height += self.velocity * self.ticksize self.velocity += self.acceleration * self.ticksize force = self.thrust_force() + self.drag_force() + self.gravity_force() self.acceleration = force / self.mass locked = False if self.mutex.tryLock(10): self.new_data.emit([self.time, self.height, self.velocity, self.acceleration]) self.mutex.unlock() self.data['time'].append(self.time) self.data['height'].append(self.height) self.data['velocity'].append(self.velocity) self.data['acceleration'].append(self.acceleration) self.update_mass() self.update_max_values() self.time += self.ticksize def drag_force(self): pressure = self.atmosphere.get_density_by_height(self.height) # Rocket is heading up if self.velocity >= 0: drag_coef = self.rocket_drag_coef area = self.cross_sectional_area # Rocket is falling with parachute deployed else: drag_coef = self.parachute_drag_coef area = self.parachute_area # Drag force is the opposite direction of velocity if self.velocity > 0: direction = -1 else: direction = 1 # TODO use increased parachute area return (direction * drag_coef * pressure * self.velocity**2 * area ) / 2 def gravity_force(self): return self.mass * self.get_g_at_alitude(self.height) def get_g_at_alitude(self, height): return self.G_0 * ((height + self.R_EARTH) / self.R_EARTH)**2 def thrust_force(self): if self.time < self.burn_length: return self.thruster.get_thrust_at_time(self.time) else: return 0 def update_mass(self): if self.time > self.burn_length: return else: self.mass -= (self.propellent_mass / self.burn_length) * self.ticksize def update_max_values(self): if self.height > self.max_height: self.max_height = self.height if self.velocity > self.max_velocity: self.max_velocity = self.velocity if self.acceleration > self.max_acceleration: self.max_acceleration = self.acceleration
class AppWindow(QDialog): @staticmethod def error_window(txt='Unknown Error!'): msg = QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText("Error") msg.setInformativeText(txt) msg.setWindowTitle("Error") msg.exec_() def __init__(self): super().__init__() self.ui = Ui_Dialog() self.ui.setupUi(self) self.canvasMutex = QMutex( ) # protects canvas (probably redundant since I don't use workers) self.timer = QtCore.QTimer() self.timer.timeout.connect(self.draw_step) # connect actions self.ui.drawPushButton.clicked.connect(self.redraw_background) self.ui.startPushButton.clicked.connect(self.start_presentation) self.ui.stopPushButton.clicked.connect(self.timer.stop) self.ui.stepPushButton.clicked.connect(self.on_step_pushed) self.ui.functionEdit.textChanged.connect(self.set_redraw_needed) self.ui.x0DoubleSpinBox.valueChanged.connect(self.set_redraw_needed) self.ui.leftIntervalSpinBox.valueChanged.connect( self.set_redraw_needed) self.ui.rightIntervalSpinBox.valueChanged.connect( self.set_redraw_needed) self.ui.xyCheckBox.clicked.connect(self.set_redraw_needed) self.eps = 1e-15 self.itTBD = 0 self.xx = None # x axes - linspace from left self.gx = None # lambda function g(x) self.xc = None # current value of x self.redraw = True # indicates that old values are no longer valid! self.show() def set_redraw_needed(self): self.redraw = True def start_presentation(self): if not self.redraw_background(): return self.itTBD = self.ui.iterationSpinBox.value() self.timer.start(int(self.ui.delaySpinBox.value() * 1000)) def on_step_pushed(self): if self.redraw and not self.redraw_background(): return self.itTBD = 1 self.draw_step() # should only be called by draw_step() def reset_drawing(self): self.timer.stop() self.itTBD = 0 self.canvasMutex.unlock() # draws one iteration on graph def draw_step(self): if self.canvasMutex.tryLock(): if self.itTBD < 1: self.reset_drawing() return nxc = self.gx(self.xc) # value of g(x_{n+1}) if abs(self.xc - nxc) < self.eps: print('I am unable to reach better precision so I\'ll stop.') self.reset_drawing() return if nxc < self.ui.leftIntervalSpinBox.value( ) or nxc > self.ui.rightIntervalSpinBox.value(): self.error_window('Out of interval!') self.reset_drawing() return # self.gx(nxc) may reach value out of interval but only on y-axis # I could also remember its value so I don't have to compute it in next iteration # but it is so cheap that I don't care self.ui.myMplCanvas.axes.plot([self.xc, nxc, nxc], [nxc, nxc, self.gx(nxc)], r'r--', linewidth=0.75) self.ui.myMplCanvas.draw() self.xc = nxc self.itTBD -= 1 self.ui.xValueLabel.setText('Current value of x is: %.6f' % self.xc) self.canvasMutex.unlock() def redraw_background(self): self.gx = lambda x: eval(self.ui.functionEdit.text()) self.xc = self.ui.x0DoubleSpinBox.value() li = self.ui.leftIntervalSpinBox.value() ri = self.ui.rightIntervalSpinBox.value() if li >= ri or self.xc < li or self.xc > ri: self.error_window('Invalid interval!') return False self.xx = np.linspace(self.ui.leftIntervalSpinBox.value(), self.ui.rightIntervalSpinBox.value(), 10000) try: yy = self.gx(self.xx) except SyntaxError: self.error_window('Unable to parse g(x)!') return False if self.canvasMutex.tryLock(): self.ui.myMplCanvas.clear() self.ui.myMplCanvas.axes.plot(self.xx, yy, label='g(x)') if self.ui.xyCheckBox.isChecked(): self.ui.myMplCanvas.axes.plot(self.xx, self.xx, label='y = x') self.ui.myMplCanvas.draw() self.redraw = False self.canvasMutex.unlock() return True