/*****************************************************************************\ Snes9x - Portable Super Nintendo Entertainment System (TM) emulator. This file is licensed under the Snes9x License. For further information, consult the LICENSE file in the root directory. \*****************************************************************************/ #include #include "gtk_s9x.h" #include "gtk_netplay_dialog.h" #include "gtk_netplay.h" #include "gtk_sound.h" uint16 MovieGetJoypad (int i); void MovieSetJoypad (int i, uint16 buttons); static uint32 local_joypads[8], joypads[8]; static GThread *npthread; extern SNPServer NPServer; static void S9xNetplayPreconnect () { S9xNetplayDisconnect (); if (gui_config->rom_loaded) { S9xAutoSaveSRAM (); } NetPlay.MaxBehindFrameCount = gui_config->netplay_max_frame_loss; NetPlay.Waiting4EmulationThread = FALSE; } static void S9xNetplayConnect () { GtkWidget *msg; S9xNetplayPreconnect (); uint32 flags = CPU.Flags; if (*(gui_config->netplay_last_rom) && top_level->try_open_rom (gui_config->netplay_last_rom)) { return; } if (!S9xNPConnectToServer (gui_config->netplay_last_host, gui_config->netplay_last_port, Memory.ROMName)) { msg = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Couldn't connect to server: %s:%d", gui_config->netplay_last_host, gui_config->netplay_last_port); gtk_window_set_title (GTK_WINDOW (msg), _("Connection Error")); gtk_dialog_run (GTK_DIALOG (msg)); gtk_widget_destroy (msg); } gui_config->netplay_activated = TRUE; /* If no rom is specified, assume we'll get it from the server */ if (*(gui_config->netplay_last_rom) == 0) { Settings.StopEmulation = FALSE; S9xROMLoaded (); } S9xReset (); CPU.Flags = flags; top_level->configure_widgets (); } void S9xNetplaySyncClients () { if (Settings.NetPlay && Settings.NetPlayServer) S9xNPServerQueueSyncAll (); } void S9xNetplayStopServer () { S9xNPStopServer (); g_thread_join (npthread); Settings.NetPlayServer = FALSE; gui_config->netplay_server_up = FALSE; } void S9xNetplayDisconnect () { if (Settings.NetPlay) { if (NetPlay.Connected) S9xNPDisconnect (); } if (gui_config->netplay_server_up) { S9xNetplayStopServer (); } gui_config->netplay_activated = FALSE; NetPlay.Paused = FALSE; top_level->configure_widgets (); } static gpointer S9xNetplayServerThread (gpointer) { S9xNPStartServer (gui_config->netplay_default_port); return NULL; } void S9xNetplayStartServer () { uint32 flags; S9xNetplayPreconnect (); flags = CPU.Flags; if (*(gui_config->netplay_last_rom) == 0 || top_level->try_open_rom (gui_config->netplay_last_rom)) { return; } Settings.NetPlayServer = TRUE; NPServer.SyncByReset = gui_config->netplay_sync_reset; NPServer.SendROMImageOnConnect = gui_config->netplay_send_rom; npthread = g_thread_new (NULL, S9xNetplayServerThread, NULL); /* Sleep to let the server create itself */ usleep (10000); S9xNPConnectToServer ("127.0.0.1", gui_config->netplay_default_port, Memory.ROMName); S9xReset (); S9xROMLoaded (); gui_config->netplay_activated = TRUE; gui_config->netplay_server_up = TRUE; CPU.Flags = flags; top_level->configure_widgets (); } void S9xNetplayDialogOpen () { Snes9xNetplayDialog *np_dialog; top_level->pause_from_focus_change (); np_dialog = new Snes9xNetplayDialog (gui_config); gtk_window_set_transient_for (np_dialog->get_window (), top_level->get_window ()); if (np_dialog->show ()) { if (!gui_config->netplay_is_server) { S9xNetplayConnect (); } else { S9xNetplayStartServer (); } S9xSoundStart (); } delete np_dialog; top_level->unpause_from_focus_change (); } int S9xNetplaySyncSpeed () { if (!Settings.NetPlay || !NetPlay.Connected) return 0; // Send 1st joypad's position update to server S9xNPSendJoypadUpdate (local_joypads[0]); // set input from network for (int i = 0; i < NP_MAX_CLIENTS; i++) joypads[i] = S9xNPGetJoypad (i); if (!S9xNPCheckForHeartBeat ()) { // No heartbeats already arrived, have to wait for one. NetPlay.PendingWait4Sync = !S9xNPWaitForHeartBeatDelay (100); IPPU.RenderThisFrame = TRUE; IPPU.SkippedFrames = 0; } else { int difference = (int) (NetPlay.MySequenceNum) - (int) (NetPlay.ServerSequenceNum); if (difference < 0) difference += 256; if (NetPlay.Waiting4EmulationThread) { if ((unsigned int) difference <= (NetPlay.MaxBehindFrameCount / 2)) { NetPlay.Waiting4EmulationThread = FALSE; S9xNPSendPause (FALSE); } } else { if ((unsigned int) difference >= (NetPlay.MaxBehindFrameCount)) { NetPlay.Waiting4EmulationThread = TRUE; S9xNPSendPause (TRUE); } } NetPlay.PendingWait4Sync = !S9xNPWaitForHeartBeatDelay (200); if (IPPU.SkippedFrames < NetPlay.MaxFrameSkip) { IPPU.SkippedFrames++; IPPU.RenderThisFrame = FALSE; } else { IPPU.RenderThisFrame = TRUE; IPPU.SkippedFrames = 0; } } if (!NetPlay.PendingWait4Sync) { NetPlay.FrameCount++; S9xNPStepJoypadHistory (); } return 1; } int S9xNetplayPush () { static int statusbar_state = FALSE; if (gui_config->netplay_activated && (!Settings.NetPlay || !NetPlay.Connected)) S9xNetplayDisconnect (); if (!Settings.NetPlay) return 0; if (NetPlay.PendingWait4Sync && !S9xNPWaitForHeartBeatDelay (100)) { S9xProcessEvents (FALSE); S9xSoundStop (); NetPlay.Paused = TRUE; if (statusbar_state == FALSE) { top_level->update_statusbar (); statusbar_state = TRUE; } return 1; } NetPlay.Paused = FALSE; if (statusbar_state) { top_level->update_statusbar (); statusbar_state = FALSE; } S9xSoundStart (); /* Save the joypad input */ for (int i = 0; i < 8; i++) { local_joypads[i] = MovieGetJoypad (i); MovieSetJoypad (i, joypads[i]); } if (NetPlay.PendingWait4Sync) { NetPlay.PendingWait4Sync = FALSE; NetPlay.FrameCount++; S9xNPStepJoypadHistory (); } return 0; } void S9xNetplayPop () { if (!Settings.NetPlay) return; for (int i = 0; i < 8; i++) MovieSetJoypad (i, local_joypads[i]); }