diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4645cfcd5a..a79e547fee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -876,6 +876,9 @@ set (PCH_SOURCES d_main.cpp d_stats.cpp d_net.cpp + d_netsingle.cpp + d_netserver.cpp + d_netclient.cpp d_netinfo.cpp d_protocol.cpp decallib.cpp diff --git a/src/actor.h b/src/actor.h index 863e4fb46a..675d406a63 100644 --- a/src/actor.h +++ b/src/actor.h @@ -48,6 +48,8 @@ #include "tflags.h" #include "portal.h" +#include "d_netsync.h" + struct subsector_t; struct FBlockNode; struct FPortalGroupArray; @@ -635,6 +637,9 @@ public: AActor &operator= (const AActor &other); ~AActor (); + NetSyncData syncdata; + NetSyncData synccompare; + virtual void Finalize(FStateDefinitions &statedef); virtual void OnDestroy() override; virtual void Serialize(FSerializer &arc) override; diff --git a/src/b_bot.cpp b/src/b_bot.cpp index 24a1bfe037..3c2998d1f8 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -212,7 +212,7 @@ CCMD (removebots) return; } - Net_WriteByte (DEM_KILLBOTS); + network->WriteByte (DEM_KILLBOTS); } CCMD (freeze) @@ -226,8 +226,8 @@ CCMD (freeze) return; } - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_FREEZE); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_FREEZE); } CCMD (listbots) diff --git a/src/b_game.cpp b/src/b_game.cpp index d21c736e51..c79958cb8b 100644 --- a/src/b_game.cpp +++ b/src/b_game.cpp @@ -314,8 +314,8 @@ bool FCajunMaster::SpawnBot (const char *name, int color) thebot->inuse = BOTINUSE_Waiting; - Net_WriteByte (DEM_ADDBOT); - Net_WriteByte (botshift); + network->WriteByte (DEM_ADDBOT); + network->WriteByte (botshift); { //Set color. char concat[512]; @@ -329,12 +329,12 @@ bool FCajunMaster::SpawnBot (const char *name, int color) mysnprintf (concat + strlen(concat), countof(concat) - strlen(concat), "\\team\\%d\n", thebot->lastteam); } - Net_WriteString (concat); + network->WriteString (concat); } - Net_WriteByte(thebot->skill.aiming); - Net_WriteByte(thebot->skill.perfection); - Net_WriteByte(thebot->skill.reaction); - Net_WriteByte(thebot->skill.isp); + network->WriteByte(thebot->skill.aiming); + network->WriteByte(thebot->skill.perfection); + network->WriteByte(thebot->skill.reaction); + network->WriteByte(thebot->skill.isp); return true; } diff --git a/src/b_think.cpp b/src/b_think.cpp index 2c41bcda36..33111d2277 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -58,9 +58,7 @@ static FRandom pr_botmove ("BotMove"); //so this is what the bot does. void DBot::Think () { - ticcmd_t *cmd = &netcmds[player - players][((gametic + 1)/ticdup)%BACKUPTICS]; - - memset (cmd, 0, sizeof(*cmd)); + ticcmd_t cmd; if (enemy && enemy->health <= 0) enemy = NULL; @@ -75,16 +73,16 @@ void DBot::Think () DAngle oldpitch = actor->Angles.Pitch; Set_enemy (); - ThinkForMove (cmd); + ThinkForMove (&cmd); TurnToAng (); - cmd->ucmd.yaw = (short)((actor->Angles.Yaw - oldyaw).Degrees * (65536 / 360.f)) / ticdup; - cmd->ucmd.pitch = (short)((oldpitch - actor->Angles.Pitch).Degrees * (65536 / 360.f)); - if (cmd->ucmd.pitch == -32768) - cmd->ucmd.pitch = -32767; - cmd->ucmd.pitch /= ticdup; - actor->Angles.Yaw = oldyaw + DAngle(cmd->ucmd.yaw * ticdup * (360 / 65536.f)); - actor->Angles.Pitch = oldpitch - DAngle(cmd->ucmd.pitch * ticdup * (360 / 65536.f)); + cmd.ucmd.yaw = (short)((actor->Angles.Yaw - oldyaw).Degrees * (65536 / 360.f)) / network->ticdup; + cmd.ucmd.pitch = (short)((oldpitch - actor->Angles.Pitch).Degrees * (65536 / 360.f)); + if (cmd.ucmd.pitch == -32768) + cmd.ucmd.pitch = -32767; + cmd.ucmd.pitch /= network->ticdup; + actor->Angles.Yaw = oldyaw + DAngle(cmd.ucmd.yaw * network->ticdup * (360 / 65536.f)); + actor->Angles.Pitch = oldpitch - DAngle(cmd.ucmd.pitch * network->ticdup * (360 / 65536.f)); } if (t_active) t_active--; @@ -101,8 +99,10 @@ void DBot::Think () } else if (player->mo->health <= 0) { // Time to respawn - cmd->ucmd.buttons |= BT_USE; + cmd.ucmd.buttons |= BT_USE; } + + network->WriteBotInput((int)(player - players), cmd); } #define THINKDISTSQ (50000.*50000./(65536.*65536.)) diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index a143e55e07..bd4c51c23d 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -117,8 +117,8 @@ CCMD (god) if (CheckCheatmode ()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_GOD); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_GOD); } CCMD(god2) @@ -126,8 +126,8 @@ CCMD(god2) if (CheckCheatmode()) return; - Net_WriteByte(DEM_GENERICCHEAT); - Net_WriteByte(CHT_GOD2); + network->WriteByte(DEM_GENERICCHEAT); + network->WriteByte(CHT_GOD2); } CCMD (iddqd) @@ -135,8 +135,8 @@ CCMD (iddqd) if (CheckCheatmode ()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_IDDQD); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_IDDQD); } CCMD (buddha) @@ -144,8 +144,8 @@ CCMD (buddha) if (CheckCheatmode()) return; - Net_WriteByte(DEM_GENERICCHEAT); - Net_WriteByte(CHT_BUDDHA); + network->WriteByte(DEM_GENERICCHEAT); + network->WriteByte(CHT_BUDDHA); } CCMD(buddha2) @@ -153,8 +153,8 @@ CCMD(buddha2) if (CheckCheatmode()) return; - Net_WriteByte(DEM_GENERICCHEAT); - Net_WriteByte(CHT_BUDDHA2); + network->WriteByte(DEM_GENERICCHEAT); + network->WriteByte(CHT_BUDDHA2); } CCMD (notarget) @@ -162,8 +162,8 @@ CCMD (notarget) if (CheckCheatmode ()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_NOTARGET); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_NOTARGET); } CCMD (fly) @@ -171,8 +171,8 @@ CCMD (fly) if (CheckCheatmode ()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_FLY); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_FLY); } /* @@ -187,8 +187,8 @@ CCMD (noclip) if (CheckCheatmode ()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_NOCLIP); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_NOCLIP); } CCMD (noclip2) @@ -196,8 +196,8 @@ CCMD (noclip2) if (CheckCheatmode()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_NOCLIP2); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_NOCLIP2); } CCMD (powerup) @@ -205,8 +205,8 @@ CCMD (powerup) if (CheckCheatmode ()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_POWER); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_POWER); } CCMD (morphme) @@ -216,13 +216,13 @@ CCMD (morphme) if (argv.argc() == 1) { - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_MORPH); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_MORPH); } else { - Net_WriteByte (DEM_MORPHEX); - Net_WriteString (argv[1]); + network->WriteByte (DEM_MORPHEX); + network->WriteString (argv[1]); } } @@ -231,8 +231,8 @@ CCMD (anubis) if (CheckCheatmode ()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_ANUBIS); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_ANUBIS); } // [GRB] @@ -241,8 +241,8 @@ CCMD (resurrect) if (CheckCheatmode ()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_RESSURECT); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_RESSURECT); } EXTERN_CVAR (Bool, chasedemo) @@ -273,8 +273,8 @@ CCMD (chase) if (gamestate != GS_LEVEL || (!(dmflags2 & DF2_CHASECAM) && deathmatch && CheckCheatmode ())) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_CHASECAM); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_CHASECAM); } } @@ -372,14 +372,14 @@ CCMD (changemap) { if (argv.argc() > 2) { - Net_WriteByte (DEM_CHANGEMAP2); - Net_WriteByte (atoi(argv[2])); + network->WriteByte (DEM_CHANGEMAP2); + network->WriteByte (atoi(argv[2])); } else { - Net_WriteByte (DEM_CHANGEMAP); + network->WriteByte (DEM_CHANGEMAP); } - Net_WriteString (mapname); + network->WriteString (mapname); } } catch(CRecoverableError &error) @@ -399,12 +399,12 @@ CCMD (give) if (CheckCheatmode () || argv.argc() < 2) return; - Net_WriteByte (DEM_GIVECHEAT); - Net_WriteString (argv[1]); + network->WriteByte (DEM_GIVECHEAT); + network->WriteString (argv[1]); if (argv.argc() > 2) - Net_WriteLong(atoi(argv[2])); + network->WriteLong(atoi(argv[2])); else - Net_WriteLong(0); + network->WriteLong(0); } CCMD (take) @@ -412,12 +412,12 @@ CCMD (take) if (CheckCheatmode () || argv.argc() < 2) return; - Net_WriteByte (DEM_TAKECHEAT); - Net_WriteString (argv[1]); + network->WriteByte (DEM_TAKECHEAT); + network->WriteString (argv[1]); if (argv.argc() > 2) - Net_WriteLong(atoi (argv[2])); + network->WriteLong(atoi (argv[2])); else - Net_WriteLong (0); + network->WriteLong (0); } CCMD(setinv) @@ -425,17 +425,17 @@ CCMD(setinv) if (CheckCheatmode() || argv.argc() < 2) return; - Net_WriteByte(DEM_SETINV); - Net_WriteString(argv[1]); + network->WriteByte(DEM_SETINV); + network->WriteString(argv[1]); if (argv.argc() > 2) - Net_WriteLong(atoi(argv[2])); + network->WriteLong(atoi(argv[2])); else - Net_WriteLong(0); + network->WriteLong(0); if (argv.argc() > 3) - Net_WriteByte(!!atoi(argv[3])); + network->WriteByte(!!atoi(argv[3])); else - Net_WriteByte(0); + network->WriteByte(0); } @@ -533,18 +533,18 @@ CCMD (puke) if (script > 0) { - Net_WriteByte (DEM_RUNSCRIPT); - Net_WriteWord (script); + network->WriteByte (DEM_RUNSCRIPT); + network->WriteWord (script); } else { - Net_WriteByte (DEM_RUNSCRIPT2); - Net_WriteWord (-script); + network->WriteByte (DEM_RUNSCRIPT2); + network->WriteWord (-script); } - Net_WriteByte (argn); + network->WriteByte (argn); for (i = 0; i < argn; ++i) { - Net_WriteLong (arg[i]); + network->WriteLong (arg[i]); } } } @@ -577,12 +577,12 @@ CCMD (pukename) arg[i] = atoi(argv[argstart + i]); } } - Net_WriteByte(DEM_RUNNAMEDSCRIPT); - Net_WriteString(argv[1]); - Net_WriteByte(argn | (always << 7)); + network->WriteByte(DEM_RUNNAMEDSCRIPT); + network->WriteString(argv[1]); + network->WriteByte(argn | (always << 7)); for (i = 0; i < argn; ++i) { - Net_WriteLong(arg[i]); + network->WriteLong(arg[i]); } } } @@ -623,12 +623,12 @@ CCMD (special) return; } } - Net_WriteByte(DEM_RUNSPECIAL); - Net_WriteWord(specnum); - Net_WriteByte(argc - 2); + network->WriteByte(DEM_RUNSPECIAL); + network->WriteWord(specnum); + network->WriteByte(argc - 2); for (int i = 2; i < argc; ++i) { - Net_WriteLong(atoi(argv[i])); + network->WriteLong(atoi(argv[i])); } } } @@ -774,10 +774,10 @@ CCMD (warp) } else { - Net_WriteByte (DEM_WARPCHEAT); - Net_WriteWord (atoi (argv[1])); - Net_WriteWord (atoi (argv[2])); - Net_WriteWord (argv.argc() == 3 ? ONFLOORZ/65536 : atoi (argv[3])); + network->WriteByte (DEM_WARPCHEAT); + network->WriteWord (atoi (argv[1])); + network->WriteWord (atoi (argv[2])); + network->WriteWord (argv.argc() == 3 ? ONFLOORZ/65536 : atoi (argv[3])); } } @@ -1078,8 +1078,8 @@ CCMD(thaw) if (CheckCheatmode()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_CLEARFROZENPROPS); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_CLEARFROZENPROPS); } //----------------------------------------------------------------------------- diff --git a/src/c_console.cpp b/src/c_console.cpp index da38a25e92..edf458abf3 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -1238,7 +1238,7 @@ void C_FullConsole () { if (demoplayback) G_CheckDemoStatus (); - D_QuitNetGame (); + network->D_QuitNetGame (); advancedemo = false; ConsoleState = c_down; HistPos = NULL; diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index 605338b013..026fab42de 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -1159,7 +1159,7 @@ void FBaseCVar::MarkUnsafe() // This type of cvar is not a "real" cvar. Instead, it gets and sets // the value of a FIntCVar, modifying it bit-by-bit. As such, it has // no default, and is not written to the .cfg or transferred around -// the network. The "host" cvar is responsible for that. +// the network-> The "host" cvar is responsible for that. // FFlagCVar::FFlagCVar (const char *name, FIntCVar &realvar, uint32_t bitval) diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index cbca6acb83..d464d67335 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -621,7 +621,7 @@ void C_DoCommand (const char *cmd, int keynum) button->ReleaseKey (keynum); if (button == &Button_Mlook && lookspring) { - Net_WriteByte (DEM_CENTERVIEW); + network->WriteByte (DEM_CENTERVIEW); } } return; diff --git a/src/ct_chat.cpp b/src/ct_chat.cpp index 85e8f0c234..941e2274b4 100644 --- a/src/ct_chat.cpp +++ b/src/ct_chat.cpp @@ -349,16 +349,16 @@ static void ShoveChatStr (const char *str, uint8_t who) who |= 2; } - Net_WriteByte (DEM_SAY); - Net_WriteByte (who); + network->WriteByte (DEM_SAY); + network->WriteByte (who); if (!chat_substitution || !DoSubstitution (substBuff, str)) { - Net_WriteString (str); + network->WriteString (str); } else { - Net_WriteString (substBuff); + network->WriteString (substBuff); } } diff --git a/src/d_event.h b/src/d_event.h index 22988cda54..0d5059a6bc 100644 --- a/src/d_event.h +++ b/src/d_event.h @@ -119,17 +119,13 @@ typedef enum // Called by IO functions when input is detected. void D_PostEvent (const event_t* ev); void D_RemoveNextCharEvent(); - +void D_AddPostedEvents(); // // GLOBAL VARIABLES // #define MAXEVENTS 128 -extern event_t events[MAXEVENTS]; -extern int eventhead; -extern int eventtail; - extern gameaction_t gameaction; diff --git a/src/d_main.cpp b/src/d_main.cpp index 668dcf1ce6..7444472ad3 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -97,6 +97,7 @@ #include "vm.h" #include "types.h" #include "r_data/r_vanillatrans.h" +#include "d_netsingle.h" EXTERN_CVAR(Bool, hud_althud) void DrawHUD(); @@ -122,9 +123,7 @@ const FIWADInfo *D_FindIWAD(TArray &wadfiles, const char *iwad, const c // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- -void D_CheckNetGame (); void D_ProcessEvents (); -void G_BuildTiccmd (ticcmd_t* cmd); void D_DoAdvanceDemo (); void D_AddWildFile (TArray &wadfiles, const char *pattern); void D_LoadWadSettings (); @@ -218,9 +217,10 @@ FString StoredWarp; bool advancedemo; FILE *debugfile; FILE *hashfile; -event_t events[MAXEVENTS]; -int eventhead; -int eventtail; +static TArray FrameStartInputEvents; +static event_t events[MAXEVENTS]; +static int eventhead; +static int eventtail; gamestate_t wipegamestate = GS_DEMOSCREEN; // can be -1 to force a wipe bool PageBlank; FTexture *Page; @@ -301,33 +301,46 @@ void D_ProcessEvents (void) void D_PostEvent (const event_t *ev) { - // Do not post duplicate consecutive EV_DeviceChange events. - if (ev->type == EV_DeviceChange && events[eventhead].type == EV_DeviceChange) + FrameStartInputEvents.Push(*ev); +} + +void D_AddPostedEvents() +{ + unsigned int c = FrameStartInputEvents.Size(); + for (unsigned int i = 0; i < c; i++) { - return; + const event_t *ev = &FrameStartInputEvents[i]; + + // Do not post duplicate consecutive EV_DeviceChange events. + if (ev->type == EV_DeviceChange && events[eventhead].type == EV_DeviceChange) + { + continue; + } + events[eventhead] = *ev; + if (ev->type == EV_Mouse && menuactive == MENU_Off && ConsoleState != c_down && ConsoleState != c_falling && !E_Responder(ev) && !paused) + { + if (Button_Mlook.bDown || freelook) + { + int look = int(ev->y * m_pitch * mouse_sensitivity * 16.0); + if (invertmouse) + look = -look; + G_AddViewPitch(look, true); + events[eventhead].y = 0; + } + if (!Button_Strafe.bDown && !lookstrafe) + { + G_AddViewAngle(int(ev->x * m_yaw * mouse_sensitivity * 8.0), true); + events[eventhead].x = 0; + } + if ((events[eventhead].x | events[eventhead].y) == 0) + { + continue; + } + } + eventhead = (eventhead + 1)&(MAXEVENTS - 1); } - events[eventhead] = *ev; - if (ev->type == EV_Mouse && menuactive == MENU_Off && ConsoleState != c_down && ConsoleState != c_falling && !E_Responder(ev) && !paused) - { - if (Button_Mlook.bDown || freelook) - { - int look = int(ev->y * m_pitch * mouse_sensitivity * 16.0); - if (invertmouse) - look = -look; - G_AddViewPitch (look, true); - events[eventhead].y = 0; - } - if (!Button_Strafe.bDown && !lookstrafe) - { - G_AddViewAngle (int(ev->x * m_yaw * mouse_sensitivity * 8.0), true); - events[eventhead].x = 0; - } - if ((events[eventhead].x | events[eventhead].y) == 0) - { - return; - } - } - eventhead = (eventhead+1)&(MAXEVENTS-1); + + FrameStartInputEvents.Clear(); } //========================================================================== @@ -380,14 +393,14 @@ CUSTOM_CVAR (Int, dmflags, 0, CVAR_SERVERINFO) if (self & DF_NO_FREELOOK) { - Net_WriteByte (DEM_CENTERVIEW); + network->WriteByte (DEM_CENTERVIEW); } // If nofov is set, force everybody to the arbitrator's FOV. if ((self & DF_NO_FOV) && consoleplayer == Net_Arbitrator) { float fov; - Net_WriteByte (DEM_FOV); + network->WriteByte (DEM_FOV); // If the game is started with DF_NO_FOV set, the arbitrator's // DesiredFOV will not be set when this callback is run, so @@ -397,7 +410,7 @@ CUSTOM_CVAR (Int, dmflags, 0, CVAR_SERVERINFO) { fov = 90; } - Net_WriteFloat (fov); + network->WriteFloat (fov); } } @@ -899,7 +912,6 @@ void D_Display () if (!wipe || NoWipe < 0) { - NetUpdate (); // send out any new accumulation // normal update // draw ZScript UI stuff C_DrawConsole (); // draw console @@ -918,7 +930,6 @@ void D_Display () screen->WipeEndScreen (); wipestart = I_msTime(); - NetUpdate(); // send out any new accumulation do { @@ -933,7 +944,6 @@ void D_Display () C_DrawConsole (); // console and M_Drawer (); // menu are drawn even on top of wipes screen->Update (); // page flip or blit buffer - NetUpdate (); // [RH] not sure this is needed anymore } while (!done); screen->WipeCleanup(); I_FreezeTime(false); @@ -955,10 +965,10 @@ void D_ErrorCleanup () { savegamerestore = false; bglobal.RemoveAllBots (true); - D_QuitNetGame (); + network->D_QuitNetGame (); if (demorecording || demoplayback) G_CheckDemoStatus (); - Net_ClearBuffers (); + network->Net_ClearBuffers (); G_NewInit (); M_ClearMenus (); singletics = false; @@ -982,53 +992,132 @@ void D_ErrorCleanup () // //========================================================================== -void D_DoomLoop () +class GameTime { - int lasttic = 0; +public: + void Update() + { + LastTic = CurrentTic; + I_SetFrameTime(); + CurrentTic = I_GetTime(); + } + int TicsElapsed() const + { + return CurrentTic - LastTic; + } + + int BaseGameTic() const + { + return LastTic; + } + + int BaseMakeTic() const + { + return LastTic + 1; + } + +private: + int LastTic = 0; + int CurrentTic = 0; +} gametime; + +class GameInput +{ +public: + void Update() + { + // Not sure why the joystick can't be updated every frame.. + bool updateJoystick = gametime.TicsElapsed() > 0; + if (updateJoystick) + { + I_StartFrame(); // To do: rename this silly function to I_UpdateJoystick + } + + // Grab input events at the beginning of the frame. + // This ensures the mouse movement matches I_GetTimeFrac precisely. + I_StartTic(); // To do: rename this to I_ProcessWindowMessages + } + + void BeforeDisplayUpdate() + { + // Apply the events we recorded in I_StartTic as the events for the next frame. + D_AddPostedEvents(); + } + +} input; + +ticcmd_t G_BuildTiccmd(); + +class PlaySim +{ +public: + void Update() + { + int tics = gametime.TicsElapsed(); + if (tics == 0) + return; + + P_UnPredictPlayer(); + + D_ProcessEvents(); + + for (int i = 0; i < tics; i++) + { + network->SetCurrentTic(gametime.BaseGameTic() + i, gametime.BaseMakeTic() + i); + network->WriteLocalInput(G_BuildTiccmd()); + + if (advancedemo) + D_DoAdvanceDemo(); + + C_Ticker(); + M_Ticker(); + G_Ticker(); + + network->EndCurrentTic(); + } + + P_PredictPlayer(&players[consoleplayer]); + + S_UpdateSounds(players[consoleplayer].camera); // move positional sounds + } + +} playsim; + +class GameDisplay +{ +public: + void Update() + { + // Render frame and present + D_Display(); + } + +} display; + +void D_DoomLoop() +{ // Clamp the timer to TICRATE until the playloop has been entered. r_NoInterpolate = true; Page = Advisory = NULL; vid_cursor.Callback(); - for (;;) + while (true) { try { - // frame syncronous IO operations - if (gametic > lasttic) - { - lasttic = gametic; - I_StartFrame (); - } - I_SetFrameTime(); + gametime.Update(); + if (netconnect) + netconnect->Update(); + network->Update(); + input.Update(); + playsim.Update(); + input.BeforeDisplayUpdate(); + display.Update(); + + GC::CheckGC(); - // process one or more tics - if (singletics) - { - I_StartTic (); - D_ProcessEvents (); - G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]); - if (advancedemo) - D_DoAdvanceDemo (); - C_Ticker (); - M_Ticker (); - G_Ticker (); - // [RH] Use the consoleplayer's camera to update sounds - S_UpdateSounds (players[consoleplayer].camera); // move positional sounds - gametic++; - maketic++; - GC::CheckGC (); - Net_NewMakeTic (); - } - else - { - TryRunTics (); // will run at least one tic - } - // Update display, next frame, with current state. - I_StartTic (); - D_Display (); if (wantToRestart) { wantToRestart = false; @@ -1037,11 +1126,11 @@ void D_DoomLoop () } catch (CRecoverableError &error) { - if (error.GetMessage ()) + if (error.GetMessage()) { - Printf (PRINT_BOLD, "\n%s\n", error.GetMessage()); + Printf(PRINT_BOLD, "\n%s\n", error.GetMessage()); } - D_ErrorCleanup (); + D_ErrorCleanup(); } catch (CVMAbortException &error) { @@ -2313,6 +2402,8 @@ void D_DoomMain (void) D_DoomInit(); + network.reset(new NetSinglePlayer()); + extern void D_ConfirmSendStats(); D_ConfirmSendStats(); @@ -2593,9 +2684,7 @@ void D_DoomMain (void) if (!restart) { - if (!batchrun) Printf ("D_CheckNetGame: Checking network game status.\n"); - StartScreen->LoadingStatus ("Checking network game status.", 0x3f); - D_CheckNetGame (); + D_SetupUserInfo(); } // [SP] Force vanilla transparency auto-detection to re-detect our game lumps now @@ -2610,7 +2699,7 @@ void D_DoomMain (void) // [RH] Run any saved commands from the command line or autoexec.cfg now. gamestate = GS_FULLCONSOLE; - Net_NewMakeTic (); + network->Startup(); DThinker::RunThinkers (); gamestate = GS_STARTUP; @@ -2693,7 +2782,7 @@ void D_DoomMain (void) G_BeginRecording(NULL); } - atterm(D_QuitNetGame); // killough + atterm([] { network->D_QuitNetGame(); }); // killough } } } @@ -2867,7 +2956,7 @@ void FStartupScreen::NetInit(char const *,int) {} void FStartupScreen::NetProgress(int) {} void FStartupScreen::NetMessage(char const *,...) {} void FStartupScreen::NetDone(void) {} -bool FStartupScreen::NetLoop(bool (*)(void *),void *) { return false; } +bool FStartupScreen::NetLoop(std::function callback) { return false; } DEFINE_FIELD_X(InputEventData, event_t, type) DEFINE_FIELD_X(InputEventData, event_t, subtype) diff --git a/src/d_net.cpp b/src/d_net.cpp index 453a1ff322..3be096d703 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -36,6 +36,8 @@ #include "c_console.h" #include "d_netinf.h" #include "d_net.h" +#include "d_netclient.h" +#include "d_netserver.h" #include "cmdlib.h" #include "m_cheat.h" #include "p_local.h" @@ -65,6 +67,9 @@ EXTERN_CVAR (Int, disableautosave) EXTERN_CVAR (Int, autosavecount) +std::unique_ptr network; +std::unique_ptr netconnect; + //#define SIMULATEERRORS (RAND_MAX/3) #define SIMULATEERRORS 0 @@ -72,16 +77,6 @@ extern uint8_t *demo_p; // [RH] Special "ticcmds" get recorded in demos extern FString savedescription; extern FString savegamefile; -extern short consistancy[MAXPLAYERS][BACKUPTICS]; - -doomcom_t doomcom; -#define netbuffer (doomcom.data) - -enum { NET_PeerToPeer, NET_PacketServer }; -uint8_t NetMode = NET_PeerToPeer; - - - // // NETWORKING // @@ -94,49 +89,12 @@ uint8_t NetMode = NET_PeerToPeer; #define RESENDCOUNT 10 #define PL_DRONE 0x80 // bit flag in doomdata->player -ticcmd_t localcmds[LOCALCMDTICS]; - -FDynamicBuffer NetSpecs[MAXPLAYERS][BACKUPTICS]; -ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; -int nettics[MAXNETNODES]; -bool nodeingame[MAXNETNODES]; // set false as nodes leave game -bool nodejustleft[MAXNETNODES]; // set when a node just left -bool remoteresend[MAXNETNODES]; // set when local needs tics -int resendto[MAXNETNODES]; // set when remote needs tics -int resendcount[MAXNETNODES]; - -uint64_t lastrecvtime[MAXPLAYERS]; // [RH] Used for pings -uint64_t currrecvtime[MAXPLAYERS]; -uint64_t lastglobalrecvtime; // Identify the last time a packet was received. -bool hadlate; -int netdelay[MAXNETNODES][BACKUPTICS]; // Used for storing network delay times. -int lastaverage; - -int nodeforplayer[MAXPLAYERS]; -int playerfornode[MAXNETNODES]; - -int maketic; -int skiptics; -int ticdup; - void D_ProcessEvents (void); -void G_BuildTiccmd (ticcmd_t *cmd); +ticcmd_t G_BuildTiccmd (); void D_DoAdvanceDemo (void); -static void SendSetup (uint32_t playersdetected[MAXNETNODES], uint8_t gotsetup[MAXNETNODES], int len); static void RunScript(uint8_t **stream, APlayerPawn *pawn, int snum, int argn, int always); -int reboundpacket; -uint8_t reboundstore[MAX_MSGLEN]; - -int frameon; -int frameskip[4]; -int oldnettics; -int mastertics; - -static int entertic; -static int oldentertics; - extern bool advancedemo; CUSTOM_CVAR (Bool, cl_capfps, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) @@ -171,1493 +129,95 @@ CVAR(Int, net_fakelatency, 0, 0); struct PacketStore { int timer; - doomcom_t message; + NetPacket message; }; static TArray InBuffer; static TArray OutBuffer; #endif -// [RH] Special "ticcmds" get stored in here -static struct TicSpecial +void Network::WriteByte(uint8_t it) { - uint8_t *streams[BACKUPTICS]; - size_t used[BACKUPTICS]; - uint8_t *streamptr; - size_t streamoffs; - size_t specialsize; - int lastmaketic; - bool okay; - - TicSpecial () - { - int i; - - lastmaketic = -1; - specialsize = 256; - - for (i = 0; i < BACKUPTICS; i++) - streams[i] = NULL; - - for (i = 0; i < BACKUPTICS; i++) - { - streams[i] = (uint8_t *)M_Malloc (256); - used[i] = 0; - } - okay = true; - } - - ~TicSpecial () - { - int i; - - for (i = 0; i < BACKUPTICS; i++) - { - if (streams[i]) - { - M_Free (streams[i]); - streams[i] = NULL; - used[i] = 0; - } - } - okay = false; - } - - // Make more room for special commands. - void GetMoreSpace (size_t needed) - { - int i; - - specialsize = MAX(specialsize * 2, needed + 30); - - DPrintf (DMSG_NOTIFY, "Expanding special size to %zu\n", specialsize); - - for (i = 0; i < BACKUPTICS; i++) - streams[i] = (uint8_t *)M_Realloc (streams[i], specialsize); - - streamptr = streams[(maketic/ticdup)%BACKUPTICS] + streamoffs; - } - - void CheckSpace (size_t needed) - { - if (streamoffs + needed >= specialsize) - GetMoreSpace (streamoffs + needed); - - streamoffs += needed; - } - - void NewMakeTic () - { - int mt = maketic / ticdup; - if (lastmaketic != -1) - { - if (lastmaketic == mt) - return; - used[lastmaketic%BACKUPTICS] = streamoffs; - } - - lastmaketic = mt; - streamptr = streams[mt%BACKUPTICS]; - streamoffs = 0; - } - - TicSpecial &operator << (uint8_t it) - { - if (streamptr) - { - CheckSpace (1); - WriteByte (it, &streamptr); - } - return *this; - } - - TicSpecial &operator << (short it) - { - if (streamptr) - { - CheckSpace (2); - WriteWord (it, &streamptr); - } - return *this; - } - - TicSpecial &operator << (int it) - { - if (streamptr) - { - CheckSpace (4); - WriteLong (it, &streamptr); - } - return *this; - } - - TicSpecial &operator << (float it) - { - if (streamptr) - { - CheckSpace (4); - WriteFloat (it, &streamptr); - } - return *this; - } - - TicSpecial &operator << (const char *it) - { - if (streamptr) - { - CheckSpace (strlen (it) + 1); - WriteString (it, &streamptr); - } - return *this; - } - -} specials; - -void Net_ClearBuffers () -{ - int i, j; - - memset (localcmds, 0, sizeof(localcmds)); - memset (netcmds, 0, sizeof(netcmds)); - memset (nettics, 0, sizeof(nettics)); - memset (nodeingame, 0, sizeof(nodeingame)); - memset (nodeforplayer, 0, sizeof(nodeforplayer)); - memset (playerfornode, 0, sizeof(playerfornode)); - memset (remoteresend, 0, sizeof(remoteresend)); - memset (resendto, 0, sizeof(resendto)); - memset (resendcount, 0, sizeof(resendcount)); - memset (lastrecvtime, 0, sizeof(lastrecvtime)); - memset (currrecvtime, 0, sizeof(currrecvtime)); - memset (consistancy, 0, sizeof(consistancy)); - nodeingame[0] = true; - - for (i = 0; i < MAXPLAYERS; i++) - { - for (j = 0; j < BACKUPTICS; j++) - { - NetSpecs[i][j].SetData (NULL, 0); - } - } - - oldentertics = entertic; - gametic = 0; - maketic = 0; - - lastglobalrecvtime = 0; + WriteBytes(&it, 1); } -// -// [RH] Rewritten to properly calculate the packet size -// with our variable length commands. -// -int NetbufferSize () +void Network::WriteWord(short it) { - if (netbuffer[0] & (NCMD_EXIT | NCMD_SETUP)) + uint16_t buf; + uint8_t *streamptr = (uint8_t*)&buf; + ::WriteWord(it, &streamptr); + WriteBytes((const uint8_t*)&buf, sizeof(uint16_t)); +} + +void Network::WriteLong(int it) +{ + uint32_t buf; + uint8_t *streamptr = (uint8_t*)&buf; + ::WriteLong(it, &streamptr); + WriteBytes((const uint8_t*)&buf, sizeof(uint32_t)); +} + +void Network::WriteFloat(float it) +{ + float buf; + uint8_t *streamptr = (uint8_t*)&buf; + ::WriteFloat(it, &streamptr); + WriteBytes((const uint8_t*)&buf, sizeof(float)); +} + +void Network::WriteString(const char *it) +{ + int length = (int)strlen(it); + WriteBytes((const uint8_t*)it, length + 1); +} + +int Network::GetHighPingThreshold() const +{ + return ((BACKUPTICS / 2 - 1) * ticdup) * (1000 / TICRATE); +} + +#if 0 // For reference. Remove when c/s migration is complete + +void Network::ReadTicCmd(uint8_t **stream, int player, int tic) +{ + int type; + uint8_t *start; + ticcmd_t *tcmd; + + int ticmod = tic % BACKUPTICS; + + tcmd = &netcmds[player][ticmod]; + tcmd->consistency = ReadWord(stream); + + start = *stream; + + while ((type = ReadByte(stream)) != DEM_USERCMD && type != DEM_EMPTYUSERCMD) + Net_SkipCommand(type, stream); + + NetSpecs[player][ticmod].SetData(start, int(*stream - start - 1)); + + if (type == DEM_USERCMD) { - return doomcom.datalength; - } - - int k = 2, count, numtics; - - if (netbuffer[0] & NCMD_RETRANSMIT) - k++; - - if (NetMode == NET_PacketServer && doomcom.remotenode == nodeforplayer[Net_Arbitrator]) - k++; - - numtics = netbuffer[0] & NCMD_XTICS; - if (numtics == 3) - { - numtics += netbuffer[k++]; - } - - if (netbuffer[0] & NCMD_QUITTERS) - { - k += netbuffer[k] + 1; - } - - // Network delay byte - k++; - - if (netbuffer[0] & NCMD_MULTI) - { - count = netbuffer[k]; - k += count; + UnpackUserCmd(&tcmd->ucmd, tic ? &netcmds[player][(tic - 1) % BACKUPTICS].ucmd : NULL, stream); } else { - count = 1; - } - - // Need at least 3 bytes per tic per player - if (doomcom.datalength < k + 3 * count * numtics) - { - return k + 3 * count * numtics; - } - - uint8_t *skipper = &netbuffer[k]; - if ((netbuffer[0] & NCMD_EXIT) == 0) - { - while (count-- > 0) + if (tic) { - SkipTicCmd (&skipper, numtics); - } - } - return int(skipper - netbuffer); -} - -// -// -// -int ExpandTics (int low) -{ - int delta; - int mt = maketic / ticdup; - - delta = low - (mt&0xff); - - if (delta >= -64 && delta <= 64) - return (mt&~0xff) + low; - if (delta > 64) - return (mt&~0xff) - 256 + low; - if (delta < -64) - return (mt&~0xff) + 256 + low; - - I_Error ("ExpandTics: strange value %i at maketic %i", low, maketic); - return 0; -} - - - -// -// HSendPacket -// -void HSendPacket (int node, int len) -{ - if (debugfile && node != 0) - { - int i, k, realretrans; - - if (netbuffer[0] & NCMD_SETUP) - { - fprintf (debugfile,"%i/%i send %i = SETUP [%3i]", gametic, maketic, node, len); - for (i = 0; i < len; i++) - fprintf (debugfile," %2x", ((uint8_t *)netbuffer)[i]); - } - else if (netbuffer[0] & NCMD_EXIT) - { - fprintf (debugfile,"%i/%i send %i = EXIT [%3i]", gametic, maketic, node, len); - for (i = 0; i < len; i++) - fprintf (debugfile," %2x", ((uint8_t *)netbuffer)[i]); + memcpy(&tcmd->ucmd, &netcmds[player][(tic - 1) % BACKUPTICS].ucmd, sizeof(tcmd->ucmd)); } else { - k = 2; - - if (NetMode == NET_PacketServer && consoleplayer == Net_Arbitrator && - node != 0) - { - k++; - } - - if (netbuffer[0] & NCMD_RETRANSMIT) - realretrans = ExpandTics (netbuffer[k++]); - else - realretrans = -1; - - int numtics = netbuffer[0] & 3; - if (numtics == 3) - numtics += netbuffer[k++]; - - fprintf (debugfile,"%i/%i send %i = (%i + %i, R %i) [%3i]", - gametic, maketic, - node, - ExpandTics(netbuffer[1]), - numtics, realretrans, len); - - for (i = 0; i < len; i++) - fprintf (debugfile, "%c%2x", i==k?'|':' ', ((uint8_t *)netbuffer)[i]); - } - fprintf (debugfile, " [[ "); - for (i = 0; i < doomcom.numnodes; ++i) - { - if (nodeingame[i]) - { - fprintf (debugfile, "%d ", nettics[i]); - } - else - { - fprintf (debugfile, "--- "); - } - } - fprintf (debugfile, "]]\n"); - } - - if (node == 0) - { - memcpy (reboundstore, netbuffer, len); - reboundpacket = len; - return; - } - - if (demoplayback) - return; - - if (!netgame) - I_Error ("Tried to transmit to another node"); - -#if SIMULATEERRORS - if (rand() < SIMULATEERRORS) - { - if (debugfile) - fprintf (debugfile, "Drop!\n"); - return; - } -#endif - - doomcom.command = CMD_SEND; - doomcom.remotenode = node; - doomcom.datalength = len; - -#ifdef _DEBUG - if (net_fakelatency / 2 > 0) - { - PacketStore store; - store.message = doomcom; - store.timer = I_GetTime() + ((net_fakelatency / 2) / (1000 / TICRATE)); - OutBuffer.Push(store); - } - else - I_NetCmd(); - - for (unsigned int i = 0; i < OutBuffer.Size(); i++) - { - if (OutBuffer[i].timer <= I_GetTime()) - { - doomcom = OutBuffer[i].message; - I_NetCmd(); - OutBuffer.Delete(i); - i = -1; + memset(&tcmd->ucmd, 0, sizeof(tcmd->ucmd)); } } -#else - I_NetCmd(); -#endif + + if (player == consoleplayer && tic>BACKUPTICS) + assert(consistency[player][ticmod] == tcmd->consistency); } -// -// HGetPacket -// Returns false if no packet is waiting -// -bool HGetPacket (void) -{ - if (reboundpacket) - { - memcpy (netbuffer, reboundstore, reboundpacket); - doomcom.remotenode = 0; - reboundpacket = 0; - return true; - } - - if (!netgame) - return false; - - if (demoplayback) - return false; - - doomcom.command = CMD_GET; - I_NetCmd (); - -#ifdef _DEBUG - if (net_fakelatency / 2 > 0 && doomcom.remotenode != -1) - { - PacketStore store; - store.message = doomcom; - store.timer = I_GetTime() + ((net_fakelatency / 2) / (1000 / TICRATE)); - InBuffer.Push(store); - doomcom.remotenode = -1; - } - - if (doomcom.remotenode == -1) - { - bool gotmessage = false; - for (unsigned int i = 0; i < InBuffer.Size(); i++) - { - if (InBuffer[i].timer <= I_GetTime()) - { - doomcom = InBuffer[i].message; - InBuffer.Delete(i); - gotmessage = true; - break; - } - } - if (!gotmessage) - return false; - } -#else - if (doomcom.remotenode == -1) - { - return false; - } -#endif - - if (debugfile) - { - int i, k, realretrans; - - if (netbuffer[0] & NCMD_SETUP) - { - fprintf (debugfile,"%i/%i get %i = SETUP [%3i]", gametic, maketic, doomcom.remotenode, doomcom.datalength); - for (i = 0; i < doomcom.datalength; i++) - fprintf (debugfile, " %2x", ((uint8_t *)netbuffer)[i]); - fprintf (debugfile, "\n"); - } - else if (netbuffer[0] & NCMD_EXIT) - { - fprintf (debugfile,"%i/%i get %i = EXIT [%3i]", gametic, maketic, doomcom.remotenode, doomcom.datalength); - for (i = 0; i < doomcom.datalength; i++) - fprintf (debugfile, " %2x", ((uint8_t *)netbuffer)[i]); - fprintf (debugfile, "\n"); - } - else { - k = 2; - - if (NetMode == NET_PacketServer && - doomcom.remotenode == nodeforplayer[Net_Arbitrator]) - { - k++; - } - - if (netbuffer[0] & NCMD_RETRANSMIT) - realretrans = ExpandTics (netbuffer[k++]); - else - realretrans = -1; - - int numtics = netbuffer[0] & 3; - if (numtics == 3) - numtics += netbuffer[k++]; - - fprintf (debugfile,"%i/%i get %i = (%i + %i, R %i) [%3i]", - gametic, maketic, - doomcom.remotenode, - ExpandTics(netbuffer[1]), - numtics, realretrans, doomcom.datalength); - - for (i = 0; i < doomcom.datalength; i++) - fprintf (debugfile, "%c%2x", i==k?'|':' ', ((uint8_t *)netbuffer)[i]); - if (numtics) - fprintf (debugfile, " <<%4x>>\n", - consistancy[playerfornode[doomcom.remotenode]][nettics[doomcom.remotenode]%BACKUPTICS] & 0xFFFF); - else - fprintf (debugfile, "\n"); - } - } - - if (doomcom.datalength != NetbufferSize ()) - { - Printf("Bad packet length %i (calculated %i)\n", - doomcom.datalength, NetbufferSize()); - - if (debugfile) - fprintf (debugfile,"---bad packet length %i (calculated %i)\n", - doomcom.datalength, NetbufferSize()); - return false; - } - - return true; -} - -void PlayerIsGone (int netnode, int netconsole) -{ - int i; - - if (nodeingame[netnode]) - { - for (i = netnode + 1; i < doomcom.numnodes; ++i) - { - if (nodeingame[i]) - break; - } - if (i == doomcom.numnodes) - { - doomcom.numnodes = netnode; - } - - if (playeringame[netconsole]) - { - players[netconsole].playerstate = PST_GONE; - } - nodeingame[netnode] = false; - nodejustleft[netnode] = false; - } - else if (nodejustleft[netnode]) // Packet Server - { - if (netnode + 1 == doomcom.numnodes) - { - doomcom.numnodes = netnode; - } - if (playeringame[netconsole]) - { - players[netconsole].playerstate = PST_GONE; - } - nodejustleft[netnode] = false; - } - else return; - - if (netconsole == Net_Arbitrator) - { - // Pick a new network arbitrator - for (int i = 0; i < MAXPLAYERS; i++) - { - if (i != netconsole && playeringame[i] && players[i].Bot == NULL) - { - Net_Arbitrator = i; - players[i].settings_controller = true; - Printf("%s is the new arbitrator\n", players[i].userinfo.GetName()); - break; - } - } - } - - if (debugfile && NetMode == NET_PacketServer) - { - if (Net_Arbitrator == consoleplayer) - { - fprintf(debugfile, "I am the new master!\n"); - } - else - { - fprintf(debugfile, "Node %d is the new master!\n", nodeforplayer[Net_Arbitrator]); - } - } - - if (demorecording) - { - G_CheckDemoStatus (); - - //WriteByte (DEM_DROPPLAYER, &demo_p); - //WriteByte ((uint8_t)netconsole, &demo_p); - } -} - -// -// GetPackets -// - -void GetPackets (void) -{ - int netconsole; - int netnode; - int realend; - int realstart; - int numtics; - int retransmitfrom; - int k; - uint8_t playerbytes[MAXNETNODES]; - int numplayers; - - while ( HGetPacket() ) - { - if (netbuffer[0] & NCMD_SETUP) - { - if (consoleplayer == Net_Arbitrator) - { - // This player apparantly doesn't realise the game has started - netbuffer[0] = NCMD_SETUP+3; - HSendPacket (doomcom.remotenode, 1); - } - continue; // extra setup packet - } - - netnode = doomcom.remotenode; - netconsole = playerfornode[netnode] & ~PL_DRONE; - - // [RH] Get "ping" times - totally useless, since it's bound to the frequency - // packets go out at. - lastrecvtime[netconsole] = currrecvtime[netconsole]; - currrecvtime[netconsole] = I_msTime (); - - // check for exiting the game - if (netbuffer[0] & NCMD_EXIT) - { - if (!nodeingame[netnode]) - continue; - - if (NetMode != NET_PacketServer || netconsole == Net_Arbitrator) - { - PlayerIsGone (netnode, netconsole); - if (NetMode == NET_PacketServer) - { - uint8_t *foo = &netbuffer[2]; - for (int i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i]) - { - int resend = ReadLong (&foo); - if (i != consoleplayer) - { - resendto[nodeforplayer[i]] = resend; - } - } - } - } - } - else - { - nodeingame[netnode] = false; - nodejustleft[netnode] = true; - } - continue; - } - - k = 2; - - if (NetMode == NET_PacketServer && - netconsole == Net_Arbitrator && - netconsole != consoleplayer) - { - mastertics = ExpandTics (netbuffer[k++]); - } - - if (netbuffer[0] & NCMD_RETRANSMIT) - { - retransmitfrom = netbuffer[k++]; - } - else - { - retransmitfrom = 0; - } - - numtics = (netbuffer[0] & NCMD_XTICS); - if (numtics == 3) - { - numtics += netbuffer[k++]; - } - - if (netbuffer[0] & NCMD_QUITTERS) - { - numplayers = netbuffer[k++]; - for (int i = 0; i < numplayers; ++i) - { - PlayerIsGone (nodeforplayer[netbuffer[k]], netbuffer[k]); - k++; - } - } - - // Pull current network delay from node - netdelay[netnode][(nettics[netnode]+1) % BACKUPTICS] = netbuffer[k++]; - - playerbytes[0] = netconsole; - if (netbuffer[0] & NCMD_MULTI) - { - numplayers = netbuffer[k++]; - memcpy (playerbytes+1, &netbuffer[k], numplayers - 1); - k += numplayers - 1; - } - else - { - numplayers = 1; - } - - // to save bytes, only the low byte of tic numbers are sent - // Figure out what the rest of the bytes are - realstart = ExpandTics (netbuffer[1]); - realend = (realstart + numtics); - - nodeforplayer[netconsole] = netnode; - - // check for retransmit request - if (resendcount[netnode] <= 0 && (netbuffer[0] & NCMD_RETRANSMIT)) - { - resendto[netnode] = ExpandTics (retransmitfrom); - if (debugfile) - fprintf (debugfile,"retransmit from %i\n", resendto[netnode]); - resendcount[netnode] = RESENDCOUNT; - } - else - { - resendcount[netnode]--; - } - - // check for out of order / duplicated packet - if (realend == nettics[netnode]) - continue; - - if (realend < nettics[netnode]) - { - if (debugfile) - fprintf (debugfile, "out of order packet (%i + %i)\n" , - realstart, numtics); - continue; - } - - // check for a missed packet - if (realstart > nettics[netnode]) - { - // stop processing until the other system resends the missed tics - if (debugfile) - fprintf (debugfile, "missed tics from %i (%i to %i)\n", - netnode, nettics[netnode], realstart); - remoteresend[netnode] = true; - continue; - } - - // update command store from the packet - { - uint8_t *start; - int i, tics; - remoteresend[netnode] = false; - - start = &netbuffer[k]; - - for (i = 0; i < numplayers; ++i) - { - int node = nodeforplayer[playerbytes[i]]; - - SkipTicCmd (&start, nettics[node] - realstart); - for (tics = nettics[node]; tics < realend; tics++) - ReadTicCmd (&start, playerbytes[i], tics); - - nettics[nodeforplayer[playerbytes[i]]] = realend; - } - } - } -} - -// -// NetUpdate -// Builds ticcmds for console player, -// sends out a packet -// -int gametime; - -void NetUpdate (void) -{ - int lowtic; - int nowtime; - int newtics; - int i,j; - int realstart; - uint8_t *cmddata; - bool resendOnly; - - GC::CheckGC(); - - if (ticdup == 0) - { - return; - } - - // check time - nowtime = I_GetTime (); - newtics = nowtime - gametime; - gametime = nowtime; - - if (newtics <= 0) // nothing new to update - { - GetPackets (); - return; - } - - if (skiptics <= newtics) - { - newtics -= skiptics; - skiptics = 0; - } - else - { - skiptics -= newtics; - newtics = 0; - } - - // build new ticcmds for console player - for (i = 0; i < newtics; i++) - { - I_StartTic (); - D_ProcessEvents (); - if (pauseext || (maketic - gametic) / ticdup >= BACKUPTICS/2-1) - break; // can't hold any more - - //Printf ("mk:%i ",maketic); - G_BuildTiccmd (&localcmds[maketic % LOCALCMDTICS]); - maketic++; - - if (ticdup == 1 || maketic == 0) - { - Net_NewMakeTic (); - } - else - { - // Once ticdup tics have been collected, average their movements - // and combine their buttons, since they will all be sent as a - // single tic that gets duplicated ticdup times. Even with ticdup, - // tics are still collected at the normal rate so that, with the - // help of prediction, the game seems as responsive as normal. - if (maketic % ticdup != 0) - { - int mod = maketic - maketic % ticdup; - int j; - - // Update the buttons for all tics in this ticdup set as soon as - // possible so that the prediction shows jumping as correctly as - // possible. (If you press +jump in the middle of a ticdup set, - // the jump will actually begin at the beginning of the set, not - // in the middle.) - for (j = maketic-2; j >= mod; --j) - { - localcmds[j % LOCALCMDTICS].ucmd.buttons |= - localcmds[(j + 1) % LOCALCMDTICS].ucmd.buttons; - } - } - else - { - // Average the ticcmds between these tics to get the - // movement that is actually sent across the network. We - // need to update them in all the localcmds slots that - // are dupped so that prediction works properly. - int mod = maketic - ticdup; - int modp, j; - - int pitch = 0; - int yaw = 0; - int roll = 0; - int forwardmove = 0; - int sidemove = 0; - int upmove = 0; - - for (j = 0; j < ticdup; ++j) - { - modp = (mod + j) % LOCALCMDTICS; - pitch += localcmds[modp].ucmd.pitch; - yaw += localcmds[modp].ucmd.yaw; - roll += localcmds[modp].ucmd.roll; - forwardmove += localcmds[modp].ucmd.forwardmove; - sidemove += localcmds[modp].ucmd.sidemove; - upmove += localcmds[modp].ucmd.upmove; - } - - pitch /= ticdup; - yaw /= ticdup; - roll /= ticdup; - forwardmove /= ticdup; - sidemove /= ticdup; - upmove /= ticdup; - - for (j = 0; j < ticdup; ++j) - { - modp = (mod + j) % LOCALCMDTICS; - localcmds[modp].ucmd.pitch = pitch; - localcmds[modp].ucmd.yaw = yaw; - localcmds[modp].ucmd.roll = roll; - localcmds[modp].ucmd.forwardmove = forwardmove; - localcmds[modp].ucmd.sidemove = sidemove; - localcmds[modp].ucmd.upmove = upmove; - } - - Net_NewMakeTic (); - } - } - } - - if (singletics) - return; // singletic update is synchronous - - if (demoplayback) - { - resendto[0] = nettics[0] = (maketic / ticdup); - return; // Don't touch netcmd data while playing a demo, as it'll already exist. - } - - // If maketic didn't cross a ticdup boundary, only send packets - // to players waiting for resends. - resendOnly = (maketic / ticdup) == (maketic - i) / ticdup; - - // send the packet to the other nodes - int count = 1; - int quitcount = 0; - - if (consoleplayer == Net_Arbitrator) - { - if (NetMode == NET_PacketServer) - { - for (j = 0; j < MAXPLAYERS; j++) - { - if (playeringame[j] && players[j].Bot == NULL) - { - count++; - } - } - - // The loop above added the local player to the count a second time, - // and it also added the player being sent the packet to the count. - count -= 2; - - for (j = 0; j < doomcom.numnodes; ++j) - { - if (nodejustleft[j]) - { - if (count == 0) - { - PlayerIsGone (j, playerfornode[j]); - } - else - { - quitcount++; - } - } - } - - if (count == 0) - { - count = 1; - } - } - } - - for (i = 0; i < doomcom.numnodes; i++) - { - uint8_t playerbytes[MAXPLAYERS]; - - if (!nodeingame[i]) - { - continue; - } - if (NetMode == NET_PacketServer && - consoleplayer != Net_Arbitrator && - i != nodeforplayer[Net_Arbitrator] && - i != 0) - { - continue; - } - if (resendOnly && resendcount[i] <= 0 && !remoteresend[i] && nettics[i]) - { - continue; - } - - int numtics; - int k; - - lowtic = maketic / ticdup; - - netbuffer[0] = 0; - netbuffer[1] = realstart = resendto[i]; - k = 2; - - if (NetMode == NET_PacketServer && - consoleplayer == Net_Arbitrator && - i != 0) - { - for (j = 1; j < doomcom.numnodes; ++j) - { - if (nodeingame[j] && nettics[j] < lowtic && j != i) - { - lowtic = nettics[j]; - } - } - netbuffer[k++] = lowtic; - } - - numtics = MAX(0, lowtic - realstart); - if (numtics > BACKUPTICS) - I_Error ("NetUpdate: Node %d missed too many tics", i); - - switch (net_extratic) - { - case 0: - default: - resendto[i] = lowtic; break; - case 1: resendto[i] = MAX(0, lowtic - 1); break; - case 2: resendto[i] = nettics[i]; break; - } - - if (numtics == 0 && resendOnly && !remoteresend[i] && nettics[i]) - { - continue; - } - - if (remoteresend[i]) - { - netbuffer[0] |= NCMD_RETRANSMIT; - netbuffer[k++] = nettics[i]; - } - - if (numtics < 3) - { - netbuffer[0] |= numtics; - } - else - { - netbuffer[0] |= NCMD_XTICS; - netbuffer[k++] = numtics - 3; - } - - if (quitcount > 0) - { - netbuffer[0] |= NCMD_QUITTERS; - netbuffer[k++] = quitcount; - for (int l = 0; l < doomcom.numnodes; ++l) - { - if (nodejustleft[l]) - { - netbuffer[k++] = playerfornode[l]; - } - } - } - - // Send current network delay - // The number of tics we just made should be removed from the count. - netbuffer[k++] = ((maketic - numtics - gametic) / ticdup); - - if (numtics > 0) - { - int l; - - if (count > 1 && i != 0 && consoleplayer == Net_Arbitrator) - { - netbuffer[0] |= NCMD_MULTI; - netbuffer[k++] = count; - - if (NetMode == NET_PacketServer) - { - for (l = 1, j = 0; j < MAXPLAYERS; j++) - { - if (playeringame[j] && players[j].Bot == NULL && j != playerfornode[i] && j != consoleplayer) - { - playerbytes[l++] = j; - netbuffer[k++] = j; - } - } - } - } - - cmddata = &netbuffer[k]; - - for (l = 0; l < count; ++l) - { - for (j = 0; j < numtics; j++) - { - int start = realstart + j, prev = start - 1; - int localstart, localprev; - - localstart = (start * ticdup) % LOCALCMDTICS; - localprev = (prev * ticdup) % LOCALCMDTICS; - start %= BACKUPTICS; - prev %= BACKUPTICS; - - // The local player has their tics sent first, followed by - // the other players. - if (l == 0) - { - WriteWord (localcmds[localstart].consistancy, &cmddata); - // [RH] Write out special "ticcmds" before real ticcmd - if (specials.used[start]) - { - memcpy (cmddata, specials.streams[start], specials.used[start]); - cmddata += specials.used[start]; - } - WriteUserCmdMessage (&localcmds[localstart].ucmd, - localprev >= 0 ? &localcmds[localprev].ucmd : NULL, &cmddata); - } - else if (i != 0) - { - int len; - uint8_t *spec; - - WriteWord (netcmds[playerbytes[l]][start].consistancy, &cmddata); - spec = NetSpecs[playerbytes[l]][start].GetData (&len); - if (spec != NULL) - { - memcpy (cmddata, spec, len); - cmddata += len; - } - - WriteUserCmdMessage (&netcmds[playerbytes[l]][start].ucmd, - prev >= 0 ? &netcmds[playerbytes[l]][prev].ucmd : NULL, &cmddata); - } - } - } - HSendPacket (i, int(cmddata - netbuffer)); - } - else - { - HSendPacket (i, k); - } - } - - // listen for other packets - GetPackets (); - - if (!resendOnly) - { - // ideally nettics[0] should be 1 - 3 tics above lowtic - // if we are consistantly slower, speed up time - - // [RH] I had erroneously assumed frameskip[] had 4 entries - // because there were 4 players, but that's not the case at - // all. The game is comparing the lag behind the master for - // four runs of TryRunTics. If our tic count is ahead of the - // master all 4 times, the next run of NetUpdate will not - // process any new input. If we have less input than the - // master, the next run of NetUpdate will process extra tics - // (because gametime gets decremented here). - - // the key player does not adapt - if (consoleplayer != Net_Arbitrator) - { - // I'm not sure about this when using a packet server, because - // if left unmodified from the P2P version, it can make the game - // very jerky. The way I have it written right now basically means - // that it won't adapt. Fortunately, player prediction helps - // alleviate the lag somewhat. - - if (NetMode == NET_PeerToPeer) - { - int totalavg = 0; - if (net_ticbalance) - { - // Try to guess ahead the time it takes to send responses to the slowest node - int nodeavg = 0, arbavg = 0; - - for (j = 0; j < BACKUPTICS; j++) - { - arbavg += netdelay[nodeforplayer[Net_Arbitrator]][j]; - nodeavg += netdelay[0][j]; - } - arbavg /= BACKUPTICS; - nodeavg /= BACKUPTICS; - - // We shouldn't adapt if we are already the arbitrator isn't what we are waiting for, otherwise it just adds more latency - if (arbavg > nodeavg) - { - lastaverage = totalavg = ((arbavg + nodeavg) / 2); - } - else - { - // Allow room to guess two tics ahead - if (nodeavg > (arbavg + 2) && lastaverage > 0) - lastaverage--; - totalavg = lastaverage; - } - } - - mastertics = nettics[nodeforplayer[Net_Arbitrator]] + totalavg; - } - if (nettics[0] <= mastertics) - { - gametime--; - if (debugfile) fprintf(debugfile, "-"); - } - if (NetMode != NET_PacketServer) - { - frameskip[(maketic / ticdup) & 3] = (oldnettics > mastertics); - } - else - { - frameskip[(maketic / ticdup) & 3] = (oldnettics - mastertics) > 3; - } - if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3]) - { - skiptics = 1; - if (debugfile) fprintf(debugfile, "+"); - } - oldnettics = nettics[0]; - } - } -} - - -// -// D_ArbitrateNetStart -// -// User info packets look like this: -// -// 0 One byte set to NCMD_SETUP or NCMD_SETUP+1; if NCMD_SETUP+1, omit byte 9 -// 1 One byte for the player's number -//2-4 Three bytes for the game version (255,high byte,low byte) -//5-8 A bit mask for each player the sender knows about -// 9 The high bit is set if the sender got the game info -// 10 A stream of bytes with the user info -// -// The guests always send NCMD_SETUP packets, and the host always -// sends NCMD_SETUP+1 packets. -// -// Game info packets look like this: -// -// 0 One byte set to NCMD_SETUP+2 -// 1 One byte for ticdup setting -// 2 One byte for NetMode setting -// 3 String with starting map's name -// . Four bytes for the RNG seed -// . Stream containing remaining game info -// -// Finished packet looks like this: -// -// 0 One byte set to NCMD_SETUP+3 -// -// Each machine sends user info packets to the host. The host sends user -// info packets back to the other machines as well as game info packets. -// Negotiation is done when all the guests have reported to the host that -// they know about the other nodes. - -struct ArbitrateData -{ - uint32_t playersdetected[MAXNETNODES]; - uint8_t gotsetup[MAXNETNODES]; -}; - -bool DoArbitrate (void *userdata) -{ - ArbitrateData *data = (ArbitrateData *)userdata; - char *s; - uint8_t *stream; - int version; - int node; - int i, j; - - while (HGetPacket ()) - { - if (netbuffer[0] == NCMD_EXIT) - { - I_FatalError ("The game was aborted."); - } - - if (doomcom.remotenode == 0) - { - continue; - } - - if (netbuffer[0] == NCMD_SETUP || netbuffer[0] == NCMD_SETUP+1) // got user info - { - node = (netbuffer[0] == NCMD_SETUP) ? doomcom.remotenode : nodeforplayer[netbuffer[1]]; - - data->playersdetected[node] = - (netbuffer[5] << 24) | (netbuffer[6] << 16) | (netbuffer[7] << 8) | netbuffer[8]; - - if (netbuffer[0] == NCMD_SETUP) - { // Sent to host - data->gotsetup[node] = netbuffer[9] & 0x80; - stream = &netbuffer[10]; - } - else - { // Sent from host - stream = &netbuffer[9]; - } - - D_ReadUserInfoStrings (netbuffer[1], &stream, false); - if (!nodeingame[node]) - { - version = (netbuffer[2] << 16) | (netbuffer[3] << 8) | netbuffer[4]; - if (version != (0xFF0000 | NETGAMEVERSION)) - { - I_Error ("Different " GAMENAME " versions cannot play a net game"); - } - - playeringame[netbuffer[1]] = true; - nodeingame[node] = true; - - data->playersdetected[0] |= 1 << netbuffer[1]; - - StartScreen->NetMessage ("Found %s (node %d, player %d)", - players[netbuffer[1]].userinfo.GetName(), - node, netbuffer[1]+1); - } - } - else if (netbuffer[0] == NCMD_SETUP+2) // got game info - { - data->gotsetup[0] = 0x80; - - ticdup = doomcom.ticdup = netbuffer[1]; - NetMode = netbuffer[2]; - - stream = &netbuffer[3]; - s = ReadString (&stream); - startmap = s; - delete[] s; - rngseed = ReadLong (&stream); - C_ReadCVars (&stream); - } - else if (netbuffer[0] == NCMD_SETUP+3) - { - return true; - } - } - - // If everybody already knows everything, it's time to go - if (consoleplayer == Net_Arbitrator) - { - for (i = 0; i < doomcom.numnodes; ++i) - if (data->playersdetected[i] != uint32_t(1 << doomcom.numnodes) - 1 || !data->gotsetup[i]) - break; - - if (i == doomcom.numnodes) - return true; - } - - netbuffer[2] = 255; - netbuffer[3] = (NETGAMEVERSION >> 8) & 255; - netbuffer[4] = NETGAMEVERSION & 255; - netbuffer[5] = data->playersdetected[0] >> 24; - netbuffer[6] = data->playersdetected[0] >> 16; - netbuffer[7] = data->playersdetected[0] >> 8; - netbuffer[8] = data->playersdetected[0]; - - if (consoleplayer != Net_Arbitrator) - { // Send user info for the local node - netbuffer[0] = NCMD_SETUP; - netbuffer[1] = consoleplayer; - netbuffer[9] = data->gotsetup[0]; - stream = &netbuffer[10]; - D_WriteUserInfoStrings (consoleplayer, &stream, true); - SendSetup (data->playersdetected, data->gotsetup, int(stream - netbuffer)); - } - else - { // Send user info for all nodes - netbuffer[0] = NCMD_SETUP+1; - for (i = 1; i < doomcom.numnodes; ++i) - { - for (j = 0; j < doomcom.numnodes; ++j) - { - // Send info about player j to player i? - if ((data->playersdetected[0] & (1<playersdetected[i] & (1<playersdetected, data->gotsetup, int(stream - netbuffer)); - } - return false; -} - -void D_ArbitrateNetStart (void) -{ - ArbitrateData data; - int i; - - // Return right away if we're just playing with ourselves. - if (doomcom.numnodes == 1) - return; - - autostart = true; - - memset (data.playersdetected, 0, sizeof(data.playersdetected)); - memset (data.gotsetup, 0, sizeof(data.gotsetup)); - - // The arbitrator knows about himself, but the other players must - // be told about themselves, in case the host had to adjust their - // userinfo (e.g. assign them to a different team). - if (consoleplayer == Net_Arbitrator) - { - data.playersdetected[0] = 1 << consoleplayer; - } - - // Assign nodes to players. The local player is always node 0. - // If the local player is not the host, then the host is node 1. - // Any remaining players are assigned node numbers in the order - // they were detected. - playerfornode[0] = consoleplayer; - nodeforplayer[consoleplayer] = 0; - if (consoleplayer == Net_Arbitrator) - { - for (i = 1; i < doomcom.numnodes; ++i) - { - playerfornode[i] = i; - nodeforplayer[i] = i; - } - } - else - { - playerfornode[1] = 0; - nodeforplayer[0] = 1; - for (i = 1; i < doomcom.numnodes; ++i) - { - if (i < consoleplayer) - { - playerfornode[i+1] = i; - nodeforplayer[i] = i+1; - } - else if (i > consoleplayer) - { - playerfornode[i] = i; - nodeforplayer[i] = i; - } - } - } - - if (consoleplayer == Net_Arbitrator) - { - data.gotsetup[0] = 0x80; - } - - StartScreen->NetInit ("Exchanging game information", 1); - if (!StartScreen->NetLoop (DoArbitrate, &data)) - { - exit (0); - } - - if (consoleplayer == Net_Arbitrator) - { - netbuffer[0] = NCMD_SETUP+3; - SendSetup (data.playersdetected, data.gotsetup, 1); - } - - if (debugfile) - { - for (i = 0; i < doomcom.numnodes; ++i) - { - fprintf (debugfile, "player %d is on node %d\n", i, nodeforplayer[i]); - } - } - StartScreen->NetDone(); -} - -static void SendSetup (uint32_t playersdetected[MAXNETNODES], uint8_t gotsetup[MAXNETNODES], int len) -{ - if (consoleplayer != Net_Arbitrator) - { - if (playersdetected[1] & (1 << consoleplayer)) - { - HSendPacket (1, 10); - } - else - { - HSendPacket (1, len); - } - } - else - { - for (int i = 1; i < doomcom.numnodes; ++i) - { - if (!gotsetup[i] || netbuffer[0] == NCMD_SETUP+3) - { - HSendPacket (i, len); - } - } - } -} - -// -// D_CheckNetGame // Works out player numbers among the net participants -// - -void D_CheckNetGame (void) +void Network::D_CheckNetGame() { const char *v; int i; @@ -1678,19 +238,32 @@ void D_CheckNetGame (void) "\nIf the game is running well below expected speeds, use netmode 0 (P2P) instead.\n"); } - // I_InitNetwork sets doomcom and netgame - if (I_InitNetwork ()) + int port = DOOMPORT; + v = Args->CheckValue("-port"); + if (v) { - // For now, stop auto selecting PacketServer, as it's more likely to cause confusion. - //NetMode = NET_PacketServer; + port = atoi(v); + Printf("using alternate port %i\n", port); } - if (doomcom.id != DOOMCOM_ID) + + netgame = false; + multiplayer = false; + consoleplayer = 0; + + v = Args->CheckValue("-dup"); + if (v) { - I_FatalError ("Doomcom buffer invalid!"); + ticdup = clamp(atoi(v), 1, MAXTICDUP); } + else + { + ticdup = 1; + } + players[0].settings_controller = true; - consoleplayer = doomcom.consoleplayer; + /* + consoleplayer = doomcom->consoleplayer; if (consoleplayer == Net_Arbitrator) { @@ -1699,7 +272,7 @@ void D_CheckNetGame (void) { NetMode = atoi(v) != 0 ? NET_PacketServer : NET_PeerToPeer; } - if (doomcom.numnodes > 1) + if (doomcom->numnodes > 1) { Printf("Selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode. (%s)\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server", v != NULL ? "forced" : "auto"); @@ -1710,323 +283,45 @@ void D_CheckNetGame (void) net_extratic = 1; } } + */ // [RH] Setup user info - D_SetupUserInfo (); + D_SetupUserInfo(); - if (Args->CheckParm ("-debugfile")) + if (Args->CheckParm("-debugfile")) { char filename[20]; - mysnprintf (filename, countof(filename), "debug%i.txt", consoleplayer); - Printf ("debug output to: %s\n", filename); - debugfile = fopen (filename, "w"); + mysnprintf(filename, countof(filename), "debug%i.txt", consoleplayer); + Printf("debug output to: %s\n", filename); + debugfile = fopen(filename, "w"); } if (netgame) { - GameConfig->ReadNetVars (); // [RH] Read network ServerInfo cvars - D_ArbitrateNetStart (); + GameConfig->ReadNetVars(); // [RH] Read network ServerInfo cvars + //D_ArbitrateNetStart(); } - // read values out of doomcom - ticdup = doomcom.ticdup; + consoleplayer = 0; + playeringame[0] = true; + nodeingame[0] = true; - for (i = 0; i < doomcom.numplayers; i++) + /* + for (i = 0; i < doomcom->numplayers; i++) playeringame[i] = true; - for (i = 0; i < doomcom.numnodes; i++) + for (i = 0; i < doomcom->numnodes; i++) nodeingame[i] = true; - if (consoleplayer != Net_Arbitrator && doomcom.numnodes > 1) + if (consoleplayer != Net_Arbitrator && doomcom->numnodes > 1) { Printf("Arbitrator selected " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " networking mode.\n", NetMode == NET_PeerToPeer ? "peer to peer" : "packet server"); } - if (!batchrun) Printf ("player %i of %i (%i nodes)\n", - consoleplayer+1, doomcom.numplayers, doomcom.numnodes); -} - - -// -// D_QuitNetGame -// Called before quitting to leave a net game -// without hanging the other players -// -void D_QuitNetGame (void) -{ - int i, j, k; - - if (!netgame || !usergame || consoleplayer == -1 || demoplayback) - return; - - // send a bunch of packets for security - netbuffer[0] = NCMD_EXIT; - netbuffer[1] = 0; - - k = 2; - if (NetMode == NET_PacketServer && consoleplayer == Net_Arbitrator) - { - uint8_t *foo = &netbuffer[2]; - - // Let the new arbitrator know what resendto counts to use - - for (i = 0; i < MAXPLAYERS; ++i) - { - if (playeringame[i] && i != consoleplayer) - WriteLong (resendto[nodeforplayer[i]], &foo); - } - k = int(foo - netbuffer); - } - - for (i = 0; i < 4; i++) - { - if (NetMode == NET_PacketServer && consoleplayer != Net_Arbitrator) - { - HSendPacket (nodeforplayer[Net_Arbitrator], 2); - } - else - { - for (j = 1; j < doomcom.numnodes; j++) - if (nodeingame[j]) - HSendPacket (j, k); - } - I_WaitVBL (1); - } - - if (debugfile) - fclose (debugfile); -} - - - -// -// TryRunTics -// -void TryRunTics (void) -{ - int i; - int lowtic; - int realtics; - int availabletics; - int counts; - int numplaying; - - // If paused, do not eat more CPU time than we need, because it - // will all be wasted anyway. - if (pauseext) - r_NoInterpolate = true; - bool doWait = cl_capfps || r_NoInterpolate /*|| netgame*/; - - // get real tics - if (doWait) - { - entertic = I_WaitForTic (oldentertics); - } - else - { - entertic = I_GetTime (); - } - realtics = entertic - oldentertics; - oldentertics = entertic; - - // get available tics - NetUpdate (); - - if (pauseext) - return; - - lowtic = INT_MAX; - numplaying = 0; - for (i = 0; i < doomcom.numnodes; i++) - { - if (nodeingame[i]) - { - numplaying++; - if (nettics[i] < lowtic) - lowtic = nettics[i]; - } - } - - if (ticdup == 1) - { - availabletics = lowtic - gametic; - } - else - { - availabletics = lowtic - gametic / ticdup; - } - - // decide how many tics to run - if (realtics < availabletics-1) - counts = realtics+1; - else if (realtics < availabletics) - counts = realtics; - else - counts = availabletics; - - // Uncapped framerate needs seprate checks - if (counts == 0 && !doWait) - { - // Check possible stall conditions - Net_CheckLastReceived(counts); - if (realtics >= 1) - { - C_Ticker(); - M_Ticker(); - // Repredict the player for new buffered movement - P_UnPredictPlayer(); - P_PredictPlayer(&players[consoleplayer]); - } - return; - } - - if (counts < 1) - counts = 1; - - if (debugfile) - fprintf (debugfile, - "=======real: %i avail: %i game: %i\n", - realtics, availabletics, counts); - - // wait for new tics if needed - while (lowtic < gametic + counts) - { - NetUpdate (); - lowtic = INT_MAX; - - for (i = 0; i < doomcom.numnodes; i++) - if (nodeingame[i] && nettics[i] < lowtic) - lowtic = nettics[i]; - - lowtic = lowtic * ticdup; - - if (lowtic < gametic) - I_Error ("TryRunTics: lowtic < gametic"); - - // Check possible stall conditions - Net_CheckLastReceived (counts); - - // Update time returned by I_GetTime, but only if we are stuck in this loop - if (lowtic < gametic + counts) - I_SetFrameTime(); - - // don't stay in here forever -- give the menu a chance to work - if (I_GetTime () - entertic >= 1) - { - C_Ticker (); - M_Ticker (); - // Repredict the player for new buffered movement - P_UnPredictPlayer(); - P_PredictPlayer(&players[consoleplayer]); - return; - } - } - - //Tic lowtic is high enough to process this gametic. Clear all possible waiting info - hadlate = false; - for (i = 0; i < MAXPLAYERS; i++) - players[i].waiting = false; - lastglobalrecvtime = I_GetTime (); //Update the last time the game tic'd over - - // run the count tics - if (counts > 0) - { - P_UnPredictPlayer(); - while (counts--) - { - if (gametic > lowtic) - { - I_Error ("gametic>lowtic"); - } - if (advancedemo) - { - D_DoAdvanceDemo (); - } - if (debugfile) fprintf (debugfile, "run tic %d\n", gametic); - C_Ticker (); - M_Ticker (); - G_Ticker(); - gametic++; - - NetUpdate (); // check for new console commands - } - P_PredictPlayer(&players[consoleplayer]); - S_UpdateSounds (players[consoleplayer].camera); // move positional sounds - } -} - -void Net_CheckLastReceived (int counts) -{ - // [Ed850] Check to see the last time a packet was received. - // If it's longer then 3 seconds, a node has likely stalled. - if (I_GetTime() - lastglobalrecvtime >= TICRATE * 3) - { - lastglobalrecvtime = I_GetTime(); //Bump the count - - if (NetMode == NET_PeerToPeer || consoleplayer == Net_Arbitrator) - { - //Keep the local node in the for loop so we can still log any cases where the local node is /somehow/ late. - //However, we don't send a resend request for sanity reasons. - for (int i = 0; i < doomcom.numnodes; i++) - { - if (nodeingame[i] && nettics[i] <= gametic + counts) - { - if (debugfile && !players[playerfornode[i]].waiting) - fprintf(debugfile, "%i is slow (%i to %i)\n", - i, nettics[i], gametic + counts); - //Send resend request to the late node. Also mark the node as waiting to display it in the hud. - if (i != 0) - remoteresend[i] = players[playerfornode[i]].waiting = hadlate = true; - } - else - players[playerfornode[i]].waiting = false; - } - } - else - { //Send a resend request to the Arbitrator, as it's obvious we are stuck here. - if (debugfile && !players[Net_Arbitrator].waiting) - fprintf(debugfile, "Arbitrator is slow (%i to %i)\n", - nettics[nodeforplayer[Net_Arbitrator]], gametic + counts); - //Send resend request to the Arbitrator. Also mark the Arbitrator as waiting to display it in the hud. - remoteresend[nodeforplayer[Net_Arbitrator]] = players[Net_Arbitrator].waiting = hadlate = true; - } - } -} - -void Net_NewMakeTic (void) -{ - specials.NewMakeTic (); -} - -void Net_WriteByte (uint8_t it) -{ - specials << it; -} - -void Net_WriteWord (short it) -{ - specials << it; -} - -void Net_WriteLong (int it) -{ - specials << it; -} - -void Net_WriteFloat (float it) -{ - specials << it; -} - -void Net_WriteString (const char *it) -{ - specials << it; -} - -void Net_WriteBytes (const uint8_t *block, int len) -{ - while (len--) - specials << *block++; + if (!batchrun) Printf("player %i of %i (%i nodes)\n", + consoleplayer + 1, doomcom->numplayers, doomcom->numnodes); + */ } +#endif //========================================================================== // @@ -2034,10 +329,13 @@ void Net_WriteBytes (const uint8_t *block, int len) // //========================================================================== -FDynamicBuffer::FDynamicBuffer () +FDynamicBuffer::FDynamicBuffer() { - m_Data = NULL; - m_Len = m_BufferLen = 0; +} + +FDynamicBuffer::FDynamicBuffer(const FDynamicBuffer &src) +{ + SetData(src.GetData(), src.GetSize()); } FDynamicBuffer::~FDynamicBuffer () @@ -2045,11 +343,18 @@ FDynamicBuffer::~FDynamicBuffer () if (m_Data) { M_Free (m_Data); - m_Data = NULL; + m_Data = nullptr; } m_Len = m_BufferLen = 0; } +FDynamicBuffer &FDynamicBuffer::operator=(const FDynamicBuffer &src) +{ + if (this != &src) + SetData(src.GetData(), src.GetSize()); + return *this; +} + void FDynamicBuffer::SetData (const uint8_t *data, int len) { if (len > m_BufferLen) @@ -2057,7 +362,7 @@ void FDynamicBuffer::SetData (const uint8_t *data, int len) m_BufferLen = (len + 255) & ~255; m_Data = (uint8_t *)M_Realloc (m_Data, m_BufferLen); } - if (data != NULL) + if (data) { m_Len = len; memcpy (m_Data, data, len); @@ -2068,13 +373,20 @@ void FDynamicBuffer::SetData (const uint8_t *data, int len) } } -uint8_t *FDynamicBuffer::GetData (int *len) +void FDynamicBuffer::AppendData(const uint8_t *data, int len) { - if (len) - *len = m_Len; - return m_Len ? m_Data : NULL; -} + int pos = m_Len; + int neededlen = m_Len + len; + if (neededlen > m_BufferLen) + { + m_BufferLen = (neededlen + 255) & ~255; + m_Data = (uint8_t *)M_Realloc(m_Data, m_BufferLen); + } + + memcpy(m_Data + pos, data, len); + m_Len += len; +} static int KillAll(PClassActor *cls) { @@ -2124,6 +436,24 @@ static int RemoveClass(const PClass *cls) return removecount; } + +void Net_RunCommands(FDynamicBuffer &buffer, int player) +{ + int len = buffer.GetSize(); + uint8_t *stream = buffer.GetData(); + if (stream) + { + uint8_t *end = stream + len; + while (stream < end) + { + int type = ReadByte(&stream); + Net_DoCommand(type, &stream, player); + } + if (!demorecording) + buffer.SetData(nullptr, 0); + } +} + // [RH] Execute a special "ticcmd". The type byte should // have already been read, and the stream is positioned // at the beginning of the command's actual data. @@ -2450,7 +780,7 @@ void Net_DoCommand (int type, uint8_t **stream, int player) { break; } - Net_WriteByte (DEM_DOAUTOSAVE); + network->WriteByte (DEM_DOAUTOSAVE); break; case DEM_DOAUTOSAVE: @@ -2860,77 +1190,11 @@ void Net_SkipCommand (int type, uint8_t **stream) *stream += skip; } -// [RH] List "ping" times CCMD (pings) { - int i; - for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i]) - Printf ("% 4" PRId64 " %s\n", currrecvtime[i] - lastrecvtime[i], - players[i].userinfo.GetName()); + network->ListPingTimes(); } -//========================================================================== -// -// Network_Controller -// -// Implement players who have the ability to change settings in a network -// game. -// -//========================================================================== - -static void Network_Controller (int playernum, bool add) -{ - if (consoleplayer != Net_Arbitrator) - { - Printf ("This command is only accessible to the net arbitrator.\n"); - return; - } - - if (players[playernum].settings_controller && add) - { - Printf ("%s is already on the setting controller list.\n", players[playernum].userinfo.GetName()); - return; - } - - if (!players[playernum].settings_controller && !add) - { - Printf ("%s is not on the setting controller list.\n", players[playernum].userinfo.GetName()); - return; - } - - if (!playeringame[playernum]) - { - Printf ("Player (%d) not found!\n", playernum); - return; - } - - if (players[playernum].Bot != NULL) - { - Printf ("Bots cannot be added to the controller list.\n"); - return; - } - - if (playernum == Net_Arbitrator) - { - Printf ("The net arbitrator cannot have their status changed on this list.\n"); - return; - } - - if (add) - Net_WriteByte (DEM_ADDCONTROLLER); - else - Net_WriteByte (DEM_DELCONTROLLER); - - Net_WriteByte (playernum); -} - -//========================================================================== -// -// CCMD net_addcontroller -// -//========================================================================== - CCMD (net_addcontroller) { if (!netgame) @@ -2945,15 +1209,9 @@ CCMD (net_addcontroller) return; } - Network_Controller (atoi (argv[1]), true); + network->Network_Controller (atoi (argv[1]), true); } -//========================================================================== -// -// CCMD net_removecontroller -// -//========================================================================== - CCMD (net_removecontroller) { if (!netgame) @@ -2968,15 +1226,9 @@ CCMD (net_removecontroller) return; } - Network_Controller (atoi (argv[1]), false); + network->Network_Controller (atoi (argv[1]), false); } -//========================================================================== -// -// CCMD net_listcontrollers -// -//========================================================================== - CCMD (net_listcontrollers) { if (!netgame) @@ -2998,3 +1250,20 @@ CCMD (net_listcontrollers) } } } + +CCMD(connect) +{ + if (argv.argc() < 2) + { + Printf("Usage: connect \n"); + return; + } + + netconnect.reset(new NetClient(argv[1])); +} + +CCMD(hostgame) +{ + netconnect.reset(); + network.reset(new NetServer()); +} diff --git a/src/d_net.h b/src/d_net.h index 352a01eb28..dff885d506 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -3,6 +3,7 @@ // Copyright 1993-1996 id Software // Copyright 1999-2016 Randy Heit // Copyright 2002-2016 Christoph Oelckers +// Copyright 2018 Magnus Norddahl // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -17,141 +18,109 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/ // -//----------------------------------------------------------------------------- -// -// DESCRIPTION: -// Networking stuff. -// -//----------------------------------------------------------------------------- - -#ifndef __D_NET__ -#define __D_NET__ +#pragma once #include "doomtype.h" #include "doomdef.h" #include "d_protocol.h" +#include "i_net.h" +#include - -// -// Network play related stuff. -// There is a data struct that stores network -// communication related stuff, and another -// one that defines the actual packets to -// be transmitted. -// - -#define DOOMCOM_ID 0x12345678l #define MAXNETNODES 8 // max computers in a game #define BACKUPTICS 36 // number of tics to remember #define MAXTICDUP 5 #define LOCALCMDTICS (BACKUPTICS*MAXTICDUP) - -#ifdef DJGPP -// The DOS drivers provide a pretty skimpy buffer. -// Probably not enough. -#define MAX_MSGLEN (BACKUPTICS*10) -#else -#define MAX_MSGLEN 14000 -#endif - -#define CMD_SEND 1 -#define CMD_GET 2 - -// -// Network packet data. -// -struct doomcom_t -{ - uint32_t id; // should be DOOMCOM_ID - int16_t intnum; // DOOM executes an int to execute commands - -// communication between DOOM and the driver - int16_t command; // CMD_SEND or CMD_GET - int16_t remotenode; // dest for send, set by get (-1 = no packet). - int16_t datalength; // bytes in doomdata to be sent - -// info common to all nodes - int16_t numnodes; // console is always node 0. - int16_t ticdup; // 1 = no duplication, 2-5 = dup for slow nets -#ifdef DJGPP - int16_t pad[5]; // keep things aligned for DOS drivers -#endif - -// info specific to this node - int16_t consoleplayer; - int16_t numplayers; -#ifdef DJGPP - int16_t angleoffset; // does not work, but needed to preserve - int16_t drone; // alignment for DOS drivers -#endif - -// packet data to be sent - uint8_t data[MAX_MSGLEN]; - -}; - - class FDynamicBuffer { public: - FDynamicBuffer (); - ~FDynamicBuffer (); + FDynamicBuffer(); + FDynamicBuffer(const FDynamicBuffer &src); + ~FDynamicBuffer(); - void SetData (const uint8_t *data, int len); - uint8_t *GetData (int *len = NULL); + FDynamicBuffer &operator=(const FDynamicBuffer &src); + + void Clear() { SetData(nullptr, 0); } + void SetData(const uint8_t *data, int len); + void AppendData(const uint8_t *data, int len); + + uint8_t *GetData() { return m_Len ? m_Data : nullptr; } + const uint8_t *GetData() const { return m_Len ? m_Data : nullptr; } + int GetSize() const { return m_Len; } private: - uint8_t *m_Data; - int m_Len, m_BufferLen; + uint8_t *m_Data = nullptr; + int m_Len = 0; + int m_BufferLen = 0; }; -extern FDynamicBuffer NetSpecs[MAXPLAYERS][BACKUPTICS]; +class Network +{ +public: + virtual ~Network() { } -// Create any new ticcmds and broadcast to other players. -void NetUpdate (void); + // Check for incoming packets + virtual void Update() = 0; -// Broadcasts special packets to other players -// to notify of game exit -void D_QuitNetGame (void); + // Set current tic for reading and writing + virtual void SetCurrentTic(int receivetic, int sendtic) = 0; -//? how many ticks to run? -void TryRunTics (void); + // Send any pending outgoing data + virtual void EndCurrentTic() = 0; -//Use for checking to see if the netgame has stalled -void Net_CheckLastReceived(int); + // Retrieve data about the current tic + virtual int GetSendTick() const = 0; + virtual ticcmd_t GetPlayerInput(int player) const = 0; + virtual ticcmd_t GetSentInput(int tic) const = 0; -// [RH] Functions for making and using special "ticcmds" -void Net_NewMakeTic (); -void Net_WriteByte (uint8_t); -void Net_WriteWord (short); -void Net_WriteLong (int); -void Net_WriteFloat (float); -void Net_WriteString (const char *); -void Net_WriteBytes (const uint8_t *, int len); + // Run network commands for the current tic + virtual void RunCommands(int player) = 0; + + // Write outgoing data for the current tic + virtual void WriteLocalInput(ticcmd_t cmd) = 0; + virtual void WriteBotInput(int player, const ticcmd_t &cmd) = 0; + virtual void WriteBytes(const uint8_t *block, int len) = 0; + void WriteByte(uint8_t it); + void WriteWord(short it); + void WriteLong(int it); + void WriteFloat(float it); + void WriteString(const char *it); + + // Statistics + virtual int GetPing(int player) const = 0; + virtual int GetServerPing() const = 0; + int GetHighPingThreshold() const; + + // CCMDs + virtual void ListPingTimes() = 0; + virtual void Network_Controller(int playernum, bool add) = 0; + + // Old init/deinit stuff + void Startup() { } + void Net_ClearBuffers() { } + void D_QuitNetGame() { } + + // Demo recording + size_t CopySpecData(int player, uint8_t *dest, size_t dest_size) { return 0; } + + // Obsolete; only needed for p2p + bool IsInconsistent(int player, int16_t checkvalue) const { return false; } + void SetConsistency(int player, int16_t checkvalue) { } + int16_t GetConsoleConsistency() const { return 0; } + + // Should probably be removed. + int ticdup = 1; +}; + +extern std::unique_ptr network; +extern std::unique_ptr netconnect; void Net_DoCommand (int type, uint8_t **stream, int player); void Net_SkipCommand (int type, uint8_t **stream); +void Net_RunCommands (FDynamicBuffer &buffer, int player); -void Net_ClearBuffers (); - - -// Netgame stuff (buffers and pointers, i.e. indices). - -// This is the interface to the packet driver, a separate program -// in DOS, but just an abstraction here. -extern doomcom_t doomcom; - -extern struct ticcmd_t localcmds[LOCALCMDTICS]; - -extern int maketic; -extern int nettics[MAXNETNODES]; -extern int netdelay[MAXNETNODES][BACKUPTICS]; -extern int nodeforplayer[MAXPLAYERS]; - -extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; -extern int ticdup; +// Old packet format. Kept for reference. Should be removed or updated once the c/s migration is complete. // [RH] // New generic packet structure: @@ -167,7 +136,7 @@ extern int ticdup; // - The first player's consolenum is not included in this list, because it always matches the sender // // For each tic: -// Two bytes with consistancy check, followed by tic data +// Two bytes with consistency check, followed by tic data // // Setup packets are different, and are described just before D_ArbitrateNetStart(). @@ -183,4 +152,14 @@ extern int ticdup; #define NCMD_1TICS 0x01 // packet contains 1 tic #define NCMD_0TICS 0x00 // packet contains 0 tics -#endif +enum +{ + PRE_CONNECT, // Sent from guest to host for initial connection + PRE_KEEPALIVE, + PRE_DISCONNECT, // Sent from guest that aborts the game + PRE_ALLHERE, // Sent from host to guest when everybody has connected + PRE_CONACK, // Sent from host to guest to acknowledge PRE_CONNECT receipt + PRE_ALLFULL, // Sent from host to an unwanted guest + PRE_ALLHEREACK, // Sent from guest to host to acknowledge PRE_ALLHEREACK receipt + PRE_GO // Sent from host to guest to continue game startup +}; diff --git a/src/d_netclient.cpp b/src/d_netclient.cpp new file mode 100644 index 0000000000..dc1817143e --- /dev/null +++ b/src/d_netclient.cpp @@ -0,0 +1,283 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1993-1996 id Software +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2016 Christoph Oelckers +// Copyright 2018 Magnus Norddahl +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// + +#include +#include + +#include "version.h" +#include "menu/menu.h" +#include "m_random.h" +#include "i_system.h" +#include "i_video.h" +#include "i_net.h" +#include "g_game.h" +#include "doomdef.h" +#include "doomstat.h" +#include "c_console.h" +#include "d_netinf.h" +#include "d_netclient.h" +#include "d_netsingle.h" +#include "cmdlib.h" +#include "s_sound.h" +#include "m_cheat.h" +#include "p_local.h" +#include "c_dispatch.h" +#include "sbar.h" +#include "gi.h" +#include "m_misc.h" +#include "gameconfigfile.h" +#include "d_gui.h" +#include "templates.h" +#include "p_acs.h" +#include "p_trace.h" +#include "a_sharedglobal.h" +#include "st_start.h" +#include "teaminfo.h" +#include "p_conversation.h" +#include "g_level.h" +#include "d_event.h" +#include "m_argv.h" +#include "p_lnspec.h" +#include "v_video.h" +#include "p_spec.h" +#include "hardware.h" +#include "r_utility.h" +#include "a_keys.h" +#include "intermission/intermission.h" +#include "g_levellocals.h" +#include "events.h" +#include "i_time.h" + +NetClient::NetClient(FString server) +{ + Printf("Connecting to %s..\n", server.GetChars()); + + mComm = I_InitNetwork(0); + mServerNode = mComm->Connect(server); + mStatus = NodeStatus::InPreGame; + + NetPacket packet; + packet.node = mServerNode; + packet.size = 1; + packet[0] = (uint8_t)NetPacketType::ConnectRequest; + mComm->PacketSend(packet); +} + +void NetClient::Update() +{ + while (true) + { + NetPacket packet; + mComm->PacketGet(packet); + if (packet.node == -1) + break; + + if (packet.node != mServerNode) + { + mComm->Close(packet.node); + } + else if (packet.size == 0) + { + OnClose(packet); + break; + } + else + { + NetPacketType type = (NetPacketType)packet[0]; + switch (type) + { + default: OnClose(packet); break; + case NetPacketType::ConnectResponse: OnConnectResponse(packet); break; + case NetPacketType::Disconnect: OnDisconnect(packet); break; + case NetPacketType::Tic: OnTic(packet); break; + } + } + + if (mStatus == NodeStatus::Closed) + { + if (network.get() == this) + { + network.reset(new NetSinglePlayer()); + G_EndNetGame(); + } + else + { + netconnect.reset(); + } + return; + } + } +} + +void NetClient::SetCurrentTic(int receivetic, int sendtic) +{ + gametic = receivetic; + mSendTic = sendtic; +} + +void NetClient::EndCurrentTic() +{ + NetPacket packet; + packet.node = mServerNode; + packet.size = 2 + sizeof(usercmd_t); + packet[0] = (uint8_t)NetPacketType::Tic; + packet[1] = 0; // target gametic + memcpy(&packet[2], &mCurrentInput[consoleplayer].ucmd, sizeof(usercmd_t)); + mComm->PacketSend(packet); + + mCurrentCommands = mSendCommands; + mSendCommands.Clear(); +} + +int NetClient::GetSendTick() const +{ + return mSendTic; +} + +ticcmd_t NetClient::GetPlayerInput(int player) const +{ + return mCurrentInput[player]; +} + +ticcmd_t NetClient::GetSentInput(int tic) const +{ + return mSentInput[tic % BACKUPTICS]; +} + +void NetClient::RunCommands(int player) +{ + if (player == consoleplayer) + { + Net_RunCommands(mCurrentCommands, consoleplayer); + } +} + +void NetClient::WriteLocalInput(ticcmd_t cmd) +{ + mCurrentInput[consoleplayer] = cmd; + mSentInput[gametic % BACKUPTICS] = cmd; +} + +void NetClient::WriteBotInput(int player, const ticcmd_t &cmd) +{ + mCurrentInput[player] = cmd; +} + +void NetClient::WriteBytes(const uint8_t *block, int len) +{ + mSendCommands.AppendData(block, len); +} + +int NetClient::GetPing(int player) const +{ + return 0; +} + +int NetClient::GetServerPing() const +{ + return 0; +} + +void NetClient::ListPingTimes() +{ +} + +void NetClient::Network_Controller(int playernum, bool add) +{ +} + +void NetClient::OnClose(const NetPacket &packet) +{ + mComm->Close(mServerNode); + mServerNode = -1; + mStatus = NodeStatus::Closed; + + if (network.get() == this) + { + Printf("Disconnected\n"); + } + else + { + Printf("Could not connect\n"); + } +} + +void NetClient::OnConnectResponse(const NetPacket &packet) +{ + if (packet.size != 3) + return; + + int version = packet[1]; // Protocol version + if (version == 1) + { + if (packet[2] != 255) // Join accepted + { + mPlayer = packet[2]; + mStatus = NodeStatus::InGame; + + G_InitClientNetGame(mPlayer, "e1m1"); + + network = std::move(netconnect); + } + else // Server full + { + mComm->Close(mServerNode); + mServerNode = -1; + mStatus = NodeStatus::Closed; + + Printf("Could not connect: server is full!\n"); + } + } + else + { + Printf("Could not connect: version mismatch.\n"); + mComm->Close(mServerNode); + mServerNode = -1; + mStatus = NodeStatus::Closed; + } +} + +void NetClient::OnDisconnect(const NetPacket &packet) +{ + mComm->Close(packet.node); + mServerNode = -1; + mStatus = NodeStatus::Closed; +} + +void NetClient::OnTic(const NetPacket &packet) +{ + if (packet.size != 2 + sizeof(float) * 5) + return; + + int tic = packet[1]; + float x = *(float*)&packet[2]; + float y = *(float*)&packet[6]; + float z = *(float*)&packet[10]; + float yaw = *(float*)&packet[14]; + float pitch = *(float*)&packet[18]; + + if (playeringame[consoleplayer] && players[consoleplayer].mo) + { + players[consoleplayer].mo->SetXYZ(x, y, z); + players[consoleplayer].mo->Angles.Yaw = yaw; + players[consoleplayer].mo->Angles.Pitch = pitch; + } +} diff --git a/src/d_netclient.h b/src/d_netclient.h new file mode 100644 index 0000000000..e7ef5668ac --- /dev/null +++ b/src/d_netclient.h @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1993-1996 id Software +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2016 Christoph Oelckers +// Copyright 2018 Magnus Norddahl +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// + +#pragma once + +#include "d_netserver.h" + +class NetClient : public Network +{ +public: + NetClient(FString server); + + void Update() override; + + void SetCurrentTic(int receivetic, int sendtic) override; + void EndCurrentTic() override; + + int GetSendTick() const override; + ticcmd_t GetPlayerInput(int player) const override; + ticcmd_t GetSentInput(int tic) const override; + + void RunCommands(int player) override; + + void WriteLocalInput(ticcmd_t cmd) override; + void WriteBotInput(int player, const ticcmd_t &cmd) override; + void WriteBytes(const uint8_t *block, int len) override; + + int GetPing(int player) const override; + int GetServerPing() const override; + + void ListPingTimes() override; + void Network_Controller(int playernum, bool add) override; + +private: + void OnClose(const NetPacket &packet); + void OnConnectResponse(const NetPacket &packet); + void OnDisconnect(const NetPacket &packet); + void OnTic(const NetPacket &packet); + + std::unique_ptr mComm; + int mServerNode = -1; + int mPlayer = -1; + NodeStatus mStatus = NodeStatus::Closed; + int mSendTic = 0; + + ticcmd_t mCurrentInput[MAXPLAYERS]; + ticcmd_t mSentInput[BACKUPTICS]; + FDynamicBuffer mCurrentCommands; + FDynamicBuffer mSendCommands; +}; diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index 2bc59fe719..3493751c16 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -540,8 +540,8 @@ void D_UserInfoChanged (FBaseCVar *cvar) mysnprintf (foo, countof(foo), "\\%s\\%s", cvar->GetName(), escaped_val.GetChars()); - Net_WriteByte (DEM_UINFCHANGED); - Net_WriteString (foo); + network->WriteByte (DEM_UINFCHANGED); + network->WriteString (foo); } static const char *SetServerVar (char *name, ECVarType type, uint8_t **stream, bool singlebit) @@ -624,15 +624,15 @@ void D_SendServerInfoChange (const FBaseCVar *cvar, UCVarValue value, ECVarType namelen = strlen (cvar->GetName ()); - Net_WriteByte (DEM_SINFCHANGED); - Net_WriteByte ((uint8_t)(namelen | (type << 6))); - Net_WriteBytes ((uint8_t *)cvar->GetName (), (int)namelen); + network->WriteByte (DEM_SINFCHANGED); + network->WriteByte ((uint8_t)(namelen | (type << 6))); + network->WriteBytes ((uint8_t *)cvar->GetName (), (int)namelen); switch (type) { - case CVAR_Bool: Net_WriteByte (value.Bool); break; - case CVAR_Int: Net_WriteLong (value.Int); break; - case CVAR_Float: Net_WriteFloat (value.Float); break; - case CVAR_String: Net_WriteString (value.String); break; + case CVAR_Bool: network->WriteByte (value.Bool); break; + case CVAR_Int: network->WriteLong (value.Int); break; + case CVAR_Float: network->WriteFloat (value.Float); break; + case CVAR_String: network->WriteString (value.String); break; default: break; // Silence GCC } } @@ -643,10 +643,10 @@ void D_SendServerFlagChange (const FBaseCVar *cvar, int bitnum, bool set) namelen = (int)strlen (cvar->GetName ()); - Net_WriteByte (DEM_SINFCHANGEDXOR); - Net_WriteByte ((uint8_t)namelen); - Net_WriteBytes ((uint8_t *)cvar->GetName (), namelen); - Net_WriteByte (uint8_t(bitnum | (set << 5))); + network->WriteByte (DEM_SINFCHANGEDXOR); + network->WriteByte ((uint8_t)namelen); + network->WriteBytes ((uint8_t *)cvar->GetName (), namelen); + network->WriteByte (uint8_t(bitnum | (set << 5))); } void D_DoServerInfoChange (uint8_t **stream, bool singlebit) diff --git a/src/d_netserver.cpp b/src/d_netserver.cpp new file mode 100644 index 0000000000..96e02ad437 --- /dev/null +++ b/src/d_netserver.cpp @@ -0,0 +1,305 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1993-1996 id Software +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2016 Christoph Oelckers +// Copyright 2018 Magnus Norddahl +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// + +#include +#include + +#include "version.h" +#include "menu/menu.h" +#include "m_random.h" +#include "i_system.h" +#include "i_video.h" +#include "i_net.h" +#include "g_game.h" +#include "doomdef.h" +#include "doomstat.h" +#include "c_console.h" +#include "d_netinf.h" +#include "d_netserver.h" +#include "cmdlib.h" +#include "s_sound.h" +#include "m_cheat.h" +#include "p_local.h" +#include "c_dispatch.h" +#include "sbar.h" +#include "gi.h" +#include "m_misc.h" +#include "gameconfigfile.h" +#include "d_gui.h" +#include "templates.h" +#include "p_acs.h" +#include "p_trace.h" +#include "a_sharedglobal.h" +#include "st_start.h" +#include "teaminfo.h" +#include "p_conversation.h" +#include "g_level.h" +#include "d_event.h" +#include "m_argv.h" +#include "p_lnspec.h" +#include "v_video.h" +#include "p_spec.h" +#include "hardware.h" +#include "r_utility.h" +#include "a_keys.h" +#include "intermission/intermission.h" +#include "g_levellocals.h" +#include "events.h" +#include "i_time.h" + +NetServer::NetServer() +{ + Printf("Started hosting multiplayer game..\n"); + + mComm = I_InitNetwork(DOOMPORT); + + G_InitServerNetGame("e1m1"); +} + +void NetServer::Update() +{ + while (true) + { + NetPacket packet; + mComm->PacketGet(packet); + if (packet.node == -1) + break; + + NetNode &node = mNodes[packet.node]; + + if (packet.size == 0) + { + OnClose(node, packet); + } + else + { + NetPacketType type = (NetPacketType)packet[0]; + switch (type) + { + default: OnClose(node, packet); break; + case NetPacketType::ConnectRequest: OnConnectRequest(node, packet); break; + case NetPacketType::Disconnect: OnDisconnect(node, packet); break; + case NetPacketType::Tic: OnTic(node, packet); break; + } + } + } +} + +void NetServer::SetCurrentTic(int receivetic, int sendtic) +{ + gametic = receivetic; + mSendTic = sendtic; +} + +void NetServer::EndCurrentTic() +{ + for (int i = 0; i < MAXNETNODES; i++) + { + if (mNodes[i].Status == NodeStatus::InGame) + { + NetPacket packet; + packet.node = i; + packet.size = 2 + sizeof(float) * 5; + packet[0] = (uint8_t)NetPacketType::Tic; + packet[1] = gametic; + + int player = mNodes[i].Player; + if (playeringame[player] && players[player].mo) + { + *(float*)&packet[2] = (float)players[player].mo->X(); + *(float*)&packet[6] = (float)players[player].mo->Y(); + *(float*)&packet[10] = (float)players[player].mo->Z(); + *(float*)&packet[14] = (float)players[player].mo->Angles.Yaw.Degrees; + *(float*)&packet[18] = (float)players[player].mo->Angles.Pitch.Degrees; + } + else + { + *(float*)&packet[2] = 0.0f; + *(float*)&packet[6] = 0.0f; + *(float*)&packet[10] = 0.0f; + *(float*)&packet[14] = 0.0f; + *(float*)&packet[18] = 0.0f; + } + + mComm->PacketSend(packet); + } + } + + mCurrentCommands = mSendCommands; + mSendCommands.Clear(); +} + +int NetServer::GetSendTick() const +{ + return mSendTic; +} + +ticcmd_t NetServer::GetPlayerInput(int player) const +{ + return mCurrentInput[player]; +} + +ticcmd_t NetServer::GetSentInput(int tic) const +{ + return mCurrentInput[consoleplayer]; +} + +void NetServer::RunCommands(int player) +{ + if (player == consoleplayer) + { + Net_RunCommands(mCurrentCommands, consoleplayer); + } +} + +void NetServer::WriteLocalInput(ticcmd_t cmd) +{ + mCurrentInput[consoleplayer] = cmd; +} + +void NetServer::WriteBotInput(int player, const ticcmd_t &cmd) +{ + mCurrentInput[player] = cmd; +} + +void NetServer::WriteBytes(const uint8_t *block, int len) +{ + mSendCommands.AppendData(block, len); +} + +int NetServer::GetPing(int player) const +{ + return 0; +} + +int NetServer::GetServerPing() const +{ + return 0; +} + +void NetServer::ListPingTimes() +{ +#if 0 + for (int i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + Printf("% 4" PRId64 " %s\n", currrecvtime[i] - lastrecvtime[i], players[i].userinfo.GetName()); +#endif +} + +void NetServer::Network_Controller(int playernum, bool add) +{ +} + +void NetServer::OnClose(NetNode &node, const NetPacket &packet) +{ + if (node.Status == NodeStatus::InGame) + { + Printf("Player %d left the server", node.Player); + + playeringame[node.Player] = false; + players[node.Player].settings_controller = false; + node.Player = -1; + } + + node.Status = NodeStatus::Closed; + mComm->Close(packet.node); +} + +void NetServer::OnConnectRequest(NetNode &node, const NetPacket &packet) +{ + // Search for a spot in the player list + if (node.Status != NodeStatus::InGame) + { + for (int i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + { + node.Player = i; + playeringame[i] = true; + players[i].settings_controller = false; + break; + } + } + } + + if (node.Player != -1) // Join accepted. + { + Printf("Player %d joined the server", node.Player); + + mNodeForPlayer[node.Player] = packet.node; + + NetPacket response; + response.node = packet.node; + response[0] = (uint8_t)NetPacketType::ConnectResponse; + response[1] = 1; // Protocol version + response[2] = node.Player; + response.size = 3; + mComm->PacketSend(response); + + node.Status = NodeStatus::InGame; + } + else // Server is full. + { + node.Status = NodeStatus::Closed; + + NetPacket response; + response.node = packet.node; + response[0] = (uint8_t)NetPacketType::ConnectResponse; + response[1] = 1; // Protocol version + response[2] = 255; + response.size = 3; + mComm->PacketSend(response); + + node.Status = NodeStatus::Closed; + mComm->Close(packet.node); + } +} + +void NetServer::OnDisconnect(NetNode &node, const NetPacket &packet) +{ + if (node.Status == NodeStatus::InGame) + { + Printf("Player %d left the server", node.Player); + + playeringame[node.Player] = false; + players[node.Player].settings_controller = false; + node.Player = -1; + } + + node.Status = NodeStatus::Closed; + mComm->Close(packet.node); +} + +void NetServer::OnTic(NetNode &node, const NetPacket &packet) +{ + if (node.Status == NodeStatus::InGame) + { + if (packet.size != 2 + sizeof(usercmd_t)) + return; + + memcpy(&mCurrentInput[node.Player].ucmd, &packet[2], sizeof(usercmd_t)); + } + else + { + node.Status = NodeStatus::Closed; + mComm->Close(packet.node); + } +} diff --git a/src/d_netserver.h b/src/d_netserver.h new file mode 100644 index 0000000000..29acca84b1 --- /dev/null +++ b/src/d_netserver.h @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1993-1996 id Software +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2016 Christoph Oelckers +// Copyright 2018 Magnus Norddahl +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// + +#pragma once + +#include "d_net.h" + +enum class NetPacketType +{ + ConnectRequest, + ConnectResponse, + Disconnect, + Tic +}; + +enum class NodeStatus +{ + Closed, + InPreGame, + InGame +}; + +struct NetNode +{ + NodeStatus Status = NodeStatus::Closed; + + int Ping = 0; + int Gametic = 0; + int Player = -1; + + ticcmd_t PlayerMovement; + FDynamicBuffer Commands; // "NetSpecs" +}; + +class NetServer : public Network +{ +public: + NetServer(); + + void Update() override; + + void SetCurrentTic(int receivetic, int sendtic) override; + void EndCurrentTic() override; + + int GetSendTick() const override; + ticcmd_t GetPlayerInput(int player) const override; + ticcmd_t GetSentInput(int tic) const override; + + void RunCommands(int player) override; + + void WriteLocalInput(ticcmd_t cmd) override; + void WriteBotInput(int player, const ticcmd_t &cmd) override; + void WriteBytes(const uint8_t *block, int len) override; + + int GetPing(int player) const override; + int GetServerPing() const override; + + void ListPingTimes() override; + void Network_Controller(int playernum, bool add) override; + +private: + void OnClose(NetNode &node, const NetPacket &packet); + void OnConnectRequest(NetNode &node, const NetPacket &packet); + void OnDisconnect(NetNode &node, const NetPacket &packet); + void OnTic(NetNode &node, const NetPacket &packet); + + std::unique_ptr mComm; + NetNode mNodes[MAXNETNODES]; + int mNodeForPlayer[MAXPLAYERS]; + int mSendTic = 0; + + ticcmd_t mCurrentInput[MAXPLAYERS]; + FDynamicBuffer mCurrentCommands; + FDynamicBuffer mSendCommands; +}; diff --git a/src/d_netsingle.cpp b/src/d_netsingle.cpp new file mode 100644 index 0000000000..9f8357108f --- /dev/null +++ b/src/d_netsingle.cpp @@ -0,0 +1,151 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1993-1996 id Software +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2016 Christoph Oelckers +// Copyright 2018 Magnus Norddahl +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// + +#include +#include + +#include "version.h" +#include "menu/menu.h" +#include "m_random.h" +#include "i_system.h" +#include "i_video.h" +#include "i_net.h" +#include "g_game.h" +#include "doomdef.h" +#include "doomstat.h" +#include "c_console.h" +#include "d_netinf.h" +#include "d_netsingle.h" +#include "cmdlib.h" +#include "s_sound.h" +#include "m_cheat.h" +#include "p_local.h" +#include "c_dispatch.h" +#include "sbar.h" +#include "gi.h" +#include "m_misc.h" +#include "gameconfigfile.h" +#include "d_gui.h" +#include "templates.h" +#include "p_acs.h" +#include "p_trace.h" +#include "a_sharedglobal.h" +#include "st_start.h" +#include "teaminfo.h" +#include "p_conversation.h" +#include "g_level.h" +#include "d_event.h" +#include "m_argv.h" +#include "p_lnspec.h" +#include "v_video.h" +#include "p_spec.h" +#include "hardware.h" +#include "r_utility.h" +#include "a_keys.h" +#include "intermission/intermission.h" +#include "g_levellocals.h" +#include "events.h" +#include "i_time.h" + +extern bool netserver, netclient; + +NetSinglePlayer::NetSinglePlayer() +{ + netgame = false; + netclient = false; + netserver = false; + multiplayer = false; + consoleplayer = 0; + players[0].settings_controller = true; + playeringame[0] = true; +} + +void NetSinglePlayer::Update() +{ +} + +void NetSinglePlayer::SetCurrentTic(int receivetic, int sendtic) +{ + gametic = receivetic; + mSendTic = sendtic; +} + +void NetSinglePlayer::EndCurrentTic() +{ + mCurrentCommands = mSendCommands; + mSendCommands.Clear(); +} + +int NetSinglePlayer::GetSendTick() const +{ + return mSendTic; +} + +ticcmd_t NetSinglePlayer::GetPlayerInput(int player) const +{ + return mCurrentInput[player]; +} + +ticcmd_t NetSinglePlayer::GetSentInput(int tic) const +{ + return mCurrentInput[consoleplayer]; +} + +void NetSinglePlayer::RunCommands(int player) +{ + if (player == consoleplayer) + { + Net_RunCommands(mCurrentCommands, consoleplayer); + } +} + +void NetSinglePlayer::WriteLocalInput(ticcmd_t cmd) +{ + mCurrentInput[consoleplayer] = cmd; +} + +void NetSinglePlayer::WriteBotInput(int player, const ticcmd_t &cmd) +{ + mCurrentInput[player] = cmd; +} + +void NetSinglePlayer::WriteBytes(const uint8_t *block, int len) +{ + mSendCommands.AppendData(block, len); +} + +int NetSinglePlayer::GetPing(int player) const +{ + return 0; +} + +int NetSinglePlayer::GetServerPing() const +{ + return 0; +} + +void NetSinglePlayer::ListPingTimes() +{ +} + +void NetSinglePlayer::Network_Controller(int playernum, bool add) +{ +} diff --git a/src/d_netsingle.h b/src/d_netsingle.h new file mode 100644 index 0000000000..3434240b0d --- /dev/null +++ b/src/d_netsingle.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1993-1996 id Software +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2016 Christoph Oelckers +// Copyright 2018 Magnus Norddahl +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// + +#pragma once + +#include "d_net.h" + +class NetSinglePlayer : public Network +{ +public: + NetSinglePlayer(); + + void Update() override; + + void SetCurrentTic(int receivetic, int sendtic) override; + void EndCurrentTic() override; + + int GetSendTick() const override; + ticcmd_t GetPlayerInput(int player) const override; + ticcmd_t GetSentInput(int tic) const override; + + void RunCommands(int player) override; + + void WriteLocalInput(ticcmd_t cmd) override; + void WriteBotInput(int player, const ticcmd_t &cmd) override; + void WriteBytes(const uint8_t *block, int len) override; + + int GetPing(int player) const override; + int GetServerPing() const override; + + void ListPingTimes() override; + void Network_Controller(int playernum, bool add) override; + +private: + ticcmd_t mCurrentInput[MAXPLAYERS]; + FDynamicBuffer mCurrentCommands; + + int mSendTic = 0; + FDynamicBuffer mSendCommands; +}; diff --git a/src/d_netsync.h b/src/d_netsync.h new file mode 100644 index 0000000000..3c764077c0 --- /dev/null +++ b/src/d_netsync.h @@ -0,0 +1,33 @@ +#ifndef __D_NETSYNC_H__ +#define __D_NETSYNC_H__ + +struct NetSyncData { + DVector3 Pos; + DVector3 Vel; + DAngle SpriteAngle; + DAngle SpriteRotation; + DRotator Angles; + DVector2 Scale; // Scaling values; 1 is normal size + double Alpha; // Since P_CheckSight makes an alpha check this can't be a float. It has to be a double. + int sprite; // used to find patch_t and flip value + uint8_t frame; // sprite frame to draw + uint8_t effects; // [RH] see p_effect.h + FRenderStyle RenderStyle; // Style to draw this actor with + uint32_t Translation; + uint32_t RenderRequired; // current renderer must have this feature set + uint32_t RenderHidden; // current renderer must *not* have any of these features + uint32_t renderflags; // Different rendering flags + double Floorclip; // value to use for floor clipping + DAngle VisibleStartAngle; + DAngle VisibleStartPitch; + DAngle VisibleEndAngle; + DAngle VisibleEndPitch; + double Speed; + double FloatSpeed; + double CameraHeight; // Height of camera when used as such + double CameraFOV; + double StealthAlpha; // Minmum alpha for MF_STEALTH. + +}; + +#endif //__D_NETSYNC_H__ \ No newline at end of file diff --git a/src/d_protocol.cpp b/src/d_protocol.cpp index 1a0ebcc023..cb7efb44df 100644 --- a/src/d_protocol.cpp +++ b/src/d_protocol.cpp @@ -290,7 +290,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, ticcmd_t &cmd, ticcmd_ { if (arc.BeginObject(key)) { - arc("consistency", cmd.consistancy) + arc("consistency", cmd.consistency) ("ucmd", cmd.ucmd) .EndObject(); } @@ -360,7 +360,7 @@ int SkipTicCmd (uint8_t **stream, int count) { bool moreticdata = true; - flow += 2; // Skip consistancy marker + flow += 2; // Skip consistency marker while (moreticdata) { uint8_t type = *flow++; @@ -407,68 +407,6 @@ int SkipTicCmd (uint8_t **stream, int count) return skip; } -extern short consistancy[MAXPLAYERS][BACKUPTICS]; -void ReadTicCmd (uint8_t **stream, int player, int tic) -{ - int type; - uint8_t *start; - ticcmd_t *tcmd; - - int ticmod = tic % BACKUPTICS; - - tcmd = &netcmds[player][ticmod]; - tcmd->consistancy = ReadWord (stream); - - start = *stream; - - while ((type = ReadByte (stream)) != DEM_USERCMD && type != DEM_EMPTYUSERCMD) - Net_SkipCommand (type, stream); - - NetSpecs[player][ticmod].SetData (start, int(*stream - start - 1)); - - if (type == DEM_USERCMD) - { - UnpackUserCmd (&tcmd->ucmd, - tic ? &netcmds[player][(tic-1)%BACKUPTICS].ucmd : NULL, stream); - } - else - { - if (tic) - { - memcpy (&tcmd->ucmd, &netcmds[player][(tic-1)%BACKUPTICS].ucmd, sizeof(tcmd->ucmd)); - } - else - { - memset (&tcmd->ucmd, 0, sizeof(tcmd->ucmd)); - } - } - - if (player==consoleplayer&&tic>BACKUPTICS) - assert(consistancy[player][ticmod] == tcmd->consistancy); -} - -void RunNetSpecs (int player, int buf) -{ - uint8_t *stream; - int len; - - if (gametic % ticdup == 0) - { - stream = NetSpecs[player][buf].GetData (&len); - if (stream) - { - uint8_t *end = stream + len; - while (stream < end) - { - int type = ReadByte (&stream); - Net_DoCommand (type, &stream, player); - } - if (!demorecording) - NetSpecs[player][buf].SetData (NULL, 0); - } - } -} - uint8_t *lenspot; // Write the header of an IFF chunk and leave space diff --git a/src/d_protocol.h b/src/d_protocol.h index ddae331a60..db95352692 100644 --- a/src/d_protocol.h +++ b/src/d_protocol.h @@ -63,13 +63,13 @@ struct zdemoheader_s { struct usercmd_t { - uint32_t buttons; - short pitch; // up/down - short yaw; // left/right - short roll; // "tilt" - short forwardmove; - short sidemove; - short upmove; + uint32_t buttons = 0; + short pitch = 0; // up/down + short yaw = 0; // left/right + short roll = 0; // "tilt" + short forwardmove = 0; + short sidemove = 0; + short upmove = 0; }; // When transmitted, the above message is preceded by a byte @@ -238,12 +238,10 @@ int WriteUserCmdMessage (usercmd_t *ucmd, const usercmd_t *basis, uint8_t **stre struct ticcmd_t { usercmd_t ucmd; - int16_t consistancy; // checks for net game + int16_t consistency = 0; // checks for net game }; int SkipTicCmd (uint8_t **stream, int count); -void ReadTicCmd (uint8_t **stream, int player, int tic); -void RunNetSpecs (int player, int buf); int ReadByte (uint8_t **stream); int ReadWord (uint8_t **stream); diff --git a/src/doomstat.h b/src/doomstat.h index e0cd34b48b..6f6083469f 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -79,6 +79,9 @@ extern bool multiplayer; // [SP] Map dm/coop implementation - invokes fake multiplayer without bots extern bool multiplayernext; +// clientside playsim +extern bool netclient; + // Flag: true only if started as net deathmatch. EXTERN_CVAR (Int, deathmatch) diff --git a/src/events.cpp b/src/events.cpp index c520359a53..fe88a5ff94 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -153,13 +153,13 @@ bool E_SendNetworkEvent(FString name, int arg1, int arg2, int arg3, bool manual) if (gamestate != GS_LEVEL) return false; - Net_WriteByte(DEM_NETEVENT); - Net_WriteString(name); - Net_WriteByte(3); - Net_WriteLong(arg1); - Net_WriteLong(arg2); - Net_WriteLong(arg3); - Net_WriteByte(manual); + network->WriteByte(DEM_NETEVENT); + network->WriteString(name); + network->WriteByte(3); + network->WriteLong(arg1); + network->WriteLong(arg2); + network->WriteLong(arg3); + network->WriteByte(manual); return true; } diff --git a/src/g_game.cpp b/src/g_game.cpp index a73758f1c4..14d73d9334 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -77,6 +77,7 @@ #include "g_hub.h" #include "g_levellocals.h" #include "events.h" +#include "gameconfigfile.h" static FRandom pr_dmspawn ("DMSpawn"); @@ -84,7 +85,7 @@ static FRandom pr_pspawn ("PlayerSpawn"); bool G_CheckDemoStatus (void); void G_ReadDemoTiccmd (ticcmd_t *cmd, int player); -void G_WriteDemoTiccmd (ticcmd_t *cmd, int player, int buf); +void G_WriteDemoTiccmd (ticcmd_t *cmd, int player); void G_PlayerReborn (int player); void G_DoNewGame (void); @@ -149,6 +150,8 @@ bool viewactive; bool netgame; // only true if packets are broadcast bool multiplayer; bool multiplayernext = false; // [SP] Map coop/dm implementation +bool netclient; // clientside playsim +bool netserver = false; // used to enforce 'netplay = true' player_t players[MAXPLAYERS]; bool playeringame[MAXPLAYERS]; @@ -176,9 +179,6 @@ bool precache = true; // if true, load all graphics at start wbstartstruct_t wminfo; // parms for world map / intermission -short consistancy[MAXPLAYERS][BACKUPTICS]; - - #define MAXPLMOVE (forwardmove[1]) #define TURBOTHRESHOLD 12800 @@ -296,12 +296,12 @@ CCMD (slot) CCMD (centerview) { - Net_WriteByte (DEM_CENTERVIEW); + network->WriteByte (DEM_CENTERVIEW); } CCMD(crouch) { - Net_WriteByte(DEM_CROUCH); + network->WriteByte(DEM_CROUCH); } CCMD (land) @@ -544,7 +544,7 @@ static inline int joyint(double val) // or reads it from the demo buffer. // If recording a demo, write it out // -void G_BuildTiccmd (ticcmd_t *cmd) +ticcmd_t G_BuildTiccmd () { int strafe; int speed; @@ -552,12 +552,9 @@ void G_BuildTiccmd (ticcmd_t *cmd) int side; int fly; - ticcmd_t *base; + ticcmd_t cmd; - base = I_BaseTiccmd (); // empty, or external driver - *cmd = *base; - - cmd->consistancy = consistancy[consoleplayer][(maketic/ticdup)%BACKUPTICS]; + cmd.consistency = network->GetConsoleConsistency(); strafe = Button_Strafe.bDown; speed = Button_Speed.bDown ^ (int)cl_run; @@ -568,7 +565,7 @@ void G_BuildTiccmd (ticcmd_t *cmd) // and not the joystick, since we treat the joystick as // the analog device it is. if (Button_Left.bDown || Button_Right.bDown) - turnheld += ticdup; + turnheld += network->ticdup; else turnheld = 0; @@ -632,32 +629,32 @@ void G_BuildTiccmd (ticcmd_t *cmd) side -= sidemove[speed]; // buttons - if (Button_Attack.bDown) cmd->ucmd.buttons |= BT_ATTACK; - if (Button_AltAttack.bDown) cmd->ucmd.buttons |= BT_ALTATTACK; - if (Button_Use.bDown) cmd->ucmd.buttons |= BT_USE; - if (Button_Jump.bDown) cmd->ucmd.buttons |= BT_JUMP; - if (Button_Crouch.bDown) cmd->ucmd.buttons |= BT_CROUCH; - if (Button_Zoom.bDown) cmd->ucmd.buttons |= BT_ZOOM; - if (Button_Reload.bDown) cmd->ucmd.buttons |= BT_RELOAD; + if (Button_Attack.bDown) cmd.ucmd.buttons |= BT_ATTACK; + if (Button_AltAttack.bDown) cmd.ucmd.buttons |= BT_ALTATTACK; + if (Button_Use.bDown) cmd.ucmd.buttons |= BT_USE; + if (Button_Jump.bDown) cmd.ucmd.buttons |= BT_JUMP; + if (Button_Crouch.bDown) cmd.ucmd.buttons |= BT_CROUCH; + if (Button_Zoom.bDown) cmd.ucmd.buttons |= BT_ZOOM; + if (Button_Reload.bDown) cmd.ucmd.buttons |= BT_RELOAD; - if (Button_User1.bDown) cmd->ucmd.buttons |= BT_USER1; - if (Button_User2.bDown) cmd->ucmd.buttons |= BT_USER2; - if (Button_User3.bDown) cmd->ucmd.buttons |= BT_USER3; - if (Button_User4.bDown) cmd->ucmd.buttons |= BT_USER4; + if (Button_User1.bDown) cmd.ucmd.buttons |= BT_USER1; + if (Button_User2.bDown) cmd.ucmd.buttons |= BT_USER2; + if (Button_User3.bDown) cmd.ucmd.buttons |= BT_USER3; + if (Button_User4.bDown) cmd.ucmd.buttons |= BT_USER4; - if (Button_Speed.bDown) cmd->ucmd.buttons |= BT_SPEED; - if (Button_Strafe.bDown) cmd->ucmd.buttons |= BT_STRAFE; - if (Button_MoveRight.bDown) cmd->ucmd.buttons |= BT_MOVERIGHT; - if (Button_MoveLeft.bDown) cmd->ucmd.buttons |= BT_MOVELEFT; - if (Button_LookDown.bDown) cmd->ucmd.buttons |= BT_LOOKDOWN; - if (Button_LookUp.bDown) cmd->ucmd.buttons |= BT_LOOKUP; - if (Button_Back.bDown) cmd->ucmd.buttons |= BT_BACK; - if (Button_Forward.bDown) cmd->ucmd.buttons |= BT_FORWARD; - if (Button_Right.bDown) cmd->ucmd.buttons |= BT_RIGHT; - if (Button_Left.bDown) cmd->ucmd.buttons |= BT_LEFT; - if (Button_MoveDown.bDown) cmd->ucmd.buttons |= BT_MOVEDOWN; - if (Button_MoveUp.bDown) cmd->ucmd.buttons |= BT_MOVEUP; - if (Button_ShowScores.bDown) cmd->ucmd.buttons |= BT_SHOWSCORES; + if (Button_Speed.bDown) cmd.ucmd.buttons |= BT_SPEED; + if (Button_Strafe.bDown) cmd.ucmd.buttons |= BT_STRAFE; + if (Button_MoveRight.bDown) cmd.ucmd.buttons |= BT_MOVERIGHT; + if (Button_MoveLeft.bDown) cmd.ucmd.buttons |= BT_MOVELEFT; + if (Button_LookDown.bDown) cmd.ucmd.buttons |= BT_LOOKDOWN; + if (Button_LookUp.bDown) cmd.ucmd.buttons |= BT_LOOKUP; + if (Button_Back.bDown) cmd.ucmd.buttons |= BT_BACK; + if (Button_Forward.bDown) cmd.ucmd.buttons |= BT_FORWARD; + if (Button_Right.bDown) cmd.ucmd.buttons |= BT_RIGHT; + if (Button_Left.bDown) cmd.ucmd.buttons |= BT_LEFT; + if (Button_MoveDown.bDown) cmd.ucmd.buttons |= BT_MOVEDOWN; + if (Button_MoveUp.bDown) cmd.ucmd.buttons |= BT_MOVEUP; + if (Button_ShowScores.bDown) cmd.ucmd.buttons |= BT_SHOWSCORES; // Handle joysticks/game controllers. float joyaxes[NUM_JOYAXIS]; @@ -695,7 +692,7 @@ void G_BuildTiccmd (ticcmd_t *cmd) forward += (int)((float)mousey * m_forward); } - cmd->ucmd.pitch = LocalViewPitch >> 16; + cmd.ucmd.pitch = LocalViewPitch >> 16; if (SendLand) { @@ -718,10 +715,10 @@ void G_BuildTiccmd (ticcmd_t *cmd) else if (side < -MAXPLMOVE) side = -MAXPLMOVE; - cmd->ucmd.forwardmove += forward; - cmd->ucmd.sidemove += side; - cmd->ucmd.yaw = LocalViewAngle >> 16; - cmd->ucmd.upmove = fly; + cmd.ucmd.forwardmove += forward; + cmd.ucmd.sidemove += side; + cmd.ucmd.yaw = LocalViewAngle >> 16; + cmd.ucmd.upmove = fly; LocalViewAngle = 0; LocalViewPitch = 0; @@ -729,42 +726,44 @@ void G_BuildTiccmd (ticcmd_t *cmd) if (sendturn180) { sendturn180 = false; - cmd->ucmd.buttons |= BT_TURN180; + cmd.ucmd.buttons |= BT_TURN180; } if (sendpause) { sendpause = false; - Net_WriteByte (DEM_PAUSE); + network->WriteByte (DEM_PAUSE); } if (sendsave) { sendsave = false; - Net_WriteByte (DEM_SAVEGAME); - Net_WriteString (savegamefile); - Net_WriteString (savedescription); + network->WriteByte (DEM_SAVEGAME); + network->WriteString (savegamefile); + network->WriteString (savedescription); savegamefile = ""; } if (SendItemUse == (const AInventory *)1) { - Net_WriteByte (DEM_INVUSEALL); + network->WriteByte (DEM_INVUSEALL); SendItemUse = NULL; } else if (SendItemUse != NULL) { - Net_WriteByte (DEM_INVUSE); - Net_WriteLong (SendItemUse->InventoryID); + network->WriteByte (DEM_INVUSE); + network->WriteLong (SendItemUse->InventoryID); SendItemUse = NULL; } if (SendItemDrop != NULL) { - Net_WriteByte (DEM_INVDROP); - Net_WriteLong (SendItemDrop->InventoryID); - Net_WriteLong(SendItemDropAmount); + network->WriteByte (DEM_INVDROP); + network->WriteLong (SendItemDrop->InventoryID); + network->WriteLong(SendItemDropAmount); SendItemDrop = NULL; } - cmd->ucmd.forwardmove <<= 8; - cmd->ucmd.sidemove <<= 8; + cmd.ucmd.forwardmove <<= 8; + cmd.ucmd.sidemove <<= 8; + + return cmd; } //[Graf Zahl] This really helps if the mouse update rate can't be increased! @@ -863,7 +862,7 @@ static void ChangeSpy (int changespy) // has done this for you, since it could desync otherwise. if (!demoplayback) { - Net_WriteByte(DEM_REVERTCAMERA); + network->WriteByte(DEM_REVERTCAMERA); } return; } @@ -1132,10 +1131,7 @@ void G_Ticker () } } - // get commands, check consistancy, and build new consistancy check - int buf = (gametic/ticdup)%BACKUPTICS; - - // [RH] Include some random seeds and player stuff in the consistancy + // [RH] Include some random seeds and player stuff in the consistency // check, not just the player's x position like BOOM. uint32_t rngsum = FRandom::StaticSumSeeds (); @@ -1147,15 +1143,13 @@ void G_Ticker () if (playeringame[i]) { ticcmd_t *cmd = &players[i].cmd; - ticcmd_t *newcmd = &netcmds[i][buf]; + ticcmd_t newcmd = network->GetPlayerInput(i); + + network->RunCommands(i); - if ((gametic % ticdup) == 0) - { - RunNetSpecs (i, buf); - } if (demorecording) { - G_WriteDemoTiccmd (newcmd, i, buf); + G_WriteDemoTiccmd (&newcmd, i); } players[i].oldbuttons = cmd->ucmd.buttons; // If the user alt-tabbed away, paused gets set to -1. In this case, @@ -1167,7 +1161,7 @@ void G_Ticker () } else { - memcpy(cmd, newcmd, sizeof(ticcmd_t)); + *cmd = newcmd; } // check for turbo cheats @@ -1177,22 +1171,21 @@ void G_Ticker () Printf ("%s is turbo!\n", players[i].userinfo.GetName()); } - if (netgame && players[i].Bot == NULL && !demoplayback && (gametic%ticdup) == 0) + if (netgame && players[i].Bot == NULL && !demoplayback && (gametic%network->ticdup) == 0) { - //players[i].inconsistant = 0; - if (gametic > BACKUPTICS*ticdup && consistancy[i][buf] != cmd->consistancy) + if (network->IsInconsistent(i, cmd->consistency)) { - players[i].inconsistant = gametic - BACKUPTICS*ticdup; + players[i].inconsistant = gametic - BACKUPTICS*network->ticdup; } if (players[i].mo) { uint32_t sum = rngsum + int((players[i].mo->X() + players[i].mo->Y() + players[i].mo->Z())*257) + players[i].mo->Angles.Yaw.BAMs() + players[i].mo->Angles.Pitch.BAMs(); sum ^= players[i].health; - consistancy[i][buf] = sum; + network->SetConsistency(i, sum); } else { - consistancy[i][buf] = rngsum; + network->SetConsistency(i, rngsum); } } } @@ -2439,11 +2432,8 @@ CCMD (stop) extern uint8_t *lenspot; -void G_WriteDemoTiccmd (ticcmd_t *cmd, int player, int buf) +void G_WriteDemoTiccmd (ticcmd_t *cmd, int player) { - uint8_t *specdata; - int speclen; - if (stoprecording) { // use "stop" console command to end demo recording G_CheckDemoStatus (); @@ -2455,12 +2445,7 @@ void G_WriteDemoTiccmd (ticcmd_t *cmd, int player, int buf) } // [RH] Write any special "ticcmds" for this player to the demo - if ((specdata = NetSpecs[player][buf].GetData (&speclen)) && gametic % ticdup == 0) - { - memcpy (demo_p, specdata, speclen); - demo_p += speclen; - NetSpecs[player][buf].SetData (NULL, 0); - } + demo_p += network->CopySpecData(player, demo_p, maxdemosize - (demo_p - demobuffer)); // [RH] Now write out a "normal" ticcmd. WriteUserCmdMessage (&cmd->ucmd, &players[player].cmd.ucmd, &demo_p); @@ -2861,6 +2846,62 @@ void G_TimeDemo (const char* name) gameaction = (gameaction == ga_loadgame) ? ga_loadgameplaydemo : ga_playdemo; } +void G_InitServerNetGame(const char *mapname) +{ + netgame = true; + netserver = true; + netclient = false; + multiplayer = true; + multiplayernext = true; + consoleplayer = 0; + players[consoleplayer].settings_controller = true; + playeringame[consoleplayer] = true; + + GameConfig->ReadNetVars(); // [RH] Read network ServerInfo cvars + D_SetupUserInfo(); + + G_DeferedInitNew(mapname); +} + +void G_InitClientNetGame(int player, const char* mapname) +{ + netgame = true; + netserver = false; + netclient = true; + multiplayer = true; + multiplayernext = true; + consoleplayer = player; + players[consoleplayer].settings_controller = true; + playeringame[consoleplayer] = true; + + GameConfig->ReadNetVars(); // [RH] Read network ServerInfo cvars + D_SetupUserInfo(); + + G_DeferedInitNew(mapname); +} + +void G_EndNetGame() +{ + gameaction = ga_fullconsole; + + // Should we do this? + //C_RestoreCVars(); // Is this a good idea? + + P_SetupWeapons_ntohton(); + demoplayback = false; + netgame = false; + netclient = false; + multiplayer = false; + multiplayernext = false; + for (int i = 1; i < MAXPLAYERS; i++) + playeringame[i] = 0; + consoleplayer = 0; + players[0].camera = NULL; + if (StatusBar != NULL) + { + StatusBar->AttachToPlayer(&players[0]); + } +} /* =================== diff --git a/src/g_game.h b/src/g_game.h index 7ac49a413d..267c92a410 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -63,6 +63,10 @@ void G_PlayDemo (char* name); void G_TimeDemo (const char* name); bool G_CheckDemoStatus (void); +void G_InitServerNetGame(const char *mapname); +void G_InitClientNetGame(int player, const char* mapname); +void G_EndNetGame(); + void G_WorldDone (void); void G_Ticker (void); diff --git a/src/g_inventory/a_weapons.cpp b/src/g_inventory/a_weapons.cpp index 25c3918c47..76af80bd26 100644 --- a/src/g_inventory/a_weapons.cpp +++ b/src/g_inventory/a_weapons.cpp @@ -1103,15 +1103,15 @@ void FWeaponSlots::SendDifferences(int playernum, const FWeaponSlots &other) // The slots differ. Send mine. if (playernum == consoleplayer) { - Net_WriteByte(DEM_SETSLOT); + network->WriteByte(DEM_SETSLOT); } else { - Net_WriteByte(DEM_SETSLOTPNUM); - Net_WriteByte(playernum); + network->WriteByte(DEM_SETSLOTPNUM); + network->WriteByte(playernum); } - Net_WriteByte(i); - Net_WriteByte(Slots[i].Size()); + network->WriteByte(i); + network->WriteByte(Slots[i].Size()); for (j = 0; j < Slots[i].Size(); ++j) { Net_WriteWeapon(Slots[i].GetWeapon(j)); @@ -1241,9 +1241,9 @@ CCMD (setslot) Printf ("Slot %d cleared\n", slot); } - Net_WriteByte(DEM_SETSLOT); - Net_WriteByte(slot); - Net_WriteByte(argv.argc()-2); + network->WriteByte(DEM_SETSLOT); + network->WriteByte(slot); + network->WriteByte(argv.argc()-2); for (int i = 2; i < argv.argc(); i++) { Net_WriteWeapon(PClass::FindActor(argv[i])); @@ -1292,8 +1292,8 @@ CCMD (addslot) } else { - Net_WriteByte(DEM_ADDSLOT); - Net_WriteByte(slot); + network->WriteByte(DEM_ADDSLOT); + network->WriteByte(slot); Net_WriteWeapon(type); } } @@ -1368,8 +1368,8 @@ CCMD (addslotdefault) } else { - Net_WriteByte(DEM_ADDSLOTDEFAULT); - Net_WriteByte(slot); + network->WriteByte(DEM_ADDSLOTDEFAULT); + network->WriteByte(slot); Net_WriteWeapon(type); } } @@ -1440,7 +1440,7 @@ void P_SetupWeapons_ntohton() // for any game appear second, and weapons that filter for some other game // appear last. The idea here is to try to keep all the weapons that are // most likely to be used at the start of the list so that they only need -// one byte to transmit across the network. +// one byte to transmit across the network-> // //=========================================================================== @@ -1534,12 +1534,12 @@ void Net_WriteWeapon(PClassActor *type) assert(index >= 0 && index <= 32767); if (index < 128) { - Net_WriteByte(index); + network->WriteByte(index); } else { - Net_WriteByte(0x80 | index); - Net_WriteByte(index >> 7); + network->WriteByte(0x80 | index); + network->WriteByte(index >> 7); } } diff --git a/src/g_level.cpp b/src/g_level.cpp index d5a9e9489a..e4dcdff15c 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -150,6 +150,8 @@ extern bool sendpause, sendsave, sendturn180, SendLand; void *statcopy; // for statistics driver FLevelLocals level; // info about current level +extern bool netserver; // serverside playsim +extern bool netclient; // clientside playsim //========================================================================== @@ -350,7 +352,7 @@ void G_NewInit () } G_ClearSnapshots (); - netgame = false; + netgame = (netclient || netserver); multiplayer = multiplayernext; multiplayernext = false; if (demoplayback) @@ -903,7 +905,7 @@ IMPLEMENT_CLASS(DAutosaver, false, false) void DAutosaver::Tick () { - Net_WriteByte (DEM_CHECKAUTOSAVE); + network->WriteByte (DEM_CHECKAUTOSAVE); Destroy (); } diff --git a/src/g_shared/a_decals.cpp b/src/g_shared/a_decals.cpp index 19216ecd24..471ac267b0 100644 --- a/src/g_shared/a_decals.cpp +++ b/src/g_shared/a_decals.cpp @@ -695,8 +695,8 @@ CCMD (spray) return; } - Net_WriteByte (DEM_SPRAY); - Net_WriteString (argv[1]); + network->WriteByte (DEM_SPRAY); + network->WriteString (argv[1]); } void SprayDecal(AActor *shooter, const char *name, double distance) diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index a3b7c2a6de..50f574a964 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -1051,11 +1051,8 @@ static void DrawLatency() { return; } - int i, localdelay = 0, arbitratordelay = 0; - for (i = 0; i < BACKUPTICS; i++) localdelay += netdelay[0][i]; - for (i = 0; i < BACKUPTICS; i++) arbitratordelay += netdelay[nodeforplayer[Net_Arbitrator]][i]; - localdelay = ((localdelay / BACKUPTICS) * ticdup) * (1000 / TICRATE); - arbitratordelay = ((arbitratordelay / BACKUPTICS) * ticdup) * (1000 / TICRATE); + int localdelay = network->GetPing(0); + int arbitratordelay = network->GetServerPing(); int color = CR_GREEN; if (MAX(localdelay, arbitratordelay) > 200) { @@ -1065,7 +1062,7 @@ static void DrawLatency() { color = CR_ORANGE; } - if (MAX(localdelay, arbitratordelay) >= ((BACKUPTICS / 2 - 1) * ticdup) * (1000 / TICRATE)) + if (MAX(localdelay, arbitratordelay) >= network->GetHighPingThreshold()) { color = CR_RED; } diff --git a/src/g_statusbar/shared_sbar.cpp b/src/g_statusbar/shared_sbar.cpp index 6309ca65f5..f1c99c84fa 100644 --- a/src/g_statusbar/shared_sbar.cpp +++ b/src/g_statusbar/shared_sbar.cpp @@ -1214,7 +1214,7 @@ void DBaseStatusBar::DrawConsistancy () const { fprintf (debugfile, "%s as of tic %d (%d)\n", conbuff, players[1-consoleplayer].inconsistant, - players[1-consoleplayer].inconsistant/ticdup); + players[1-consoleplayer].inconsistant/network->ticdup); } } screen->DrawText (SmallFont, CR_GREEN, diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp index fd4c4123be..40296ff4f4 100644 --- a/src/hu_scores.cpp +++ b/src/hu_scores.cpp @@ -404,7 +404,7 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, { // The teamplay mode uses colors to show teams, so we need some // other way to do highlighting. And it may as well be used for - // all modes for the sake of consistancy. + // all modes for the sake of consistency. screen->Dim(MAKERGB(200,245,255), 0.125f, col1 - 12*CleanXfac, y - 1, col5 + (maxnamewidth + 24)*CleanXfac, height + 2); } @@ -431,14 +431,7 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int col1, int col2, screen->DrawText (SmallFont, color, col4, y + ypadding, player->userinfo.GetName(), DTA_CleanNoMove, true, TAG_DONE); - int avgdelay = 0; - for (int i = 0; i < BACKUPTICS; i++) - { - avgdelay += netdelay[nodeforplayer[(int)(player - players)]][i]; - } - avgdelay /= BACKUPTICS; - - mysnprintf(str, countof(str), "%d", (avgdelay * ticdup) * (1000 / TICRATE)); + mysnprintf(str, countof(str), "%d", network->GetPing((int)(player - players))); screen->DrawText(SmallFont, color, col5, y + ypadding, str, DTA_CleanNoMove, true, TAG_DONE); diff --git a/src/i_net.cpp b/src/i_net.cpp index 5c74c87aed..c53bd740f2 100644 --- a/src/i_net.cpp +++ b/src/i_net.cpp @@ -63,6 +63,7 @@ #include "m_misc.h" #include "i_net.h" +#include "i_time.h" // As per http://support.microsoft.com/kb/q192599/ the standard // size for network buffers is 8k. @@ -89,272 +90,262 @@ typedef int SOCKET; typedef int socklen_t; #endif -// -// NETWORKING -// - -static u_short DOOMPORT = (IPPORT_USERRESERVED + 29); -static SOCKET mysocket = INVALID_SOCKET; -static sockaddr_in sendaddress[MAXNETNODES]; -static uint8_t sendplayer[MAXNETNODES]; - #ifdef __WIN32__ const char *neterror (void); #else #define neterror() strerror(errno) #endif -enum +class DoomComImpl : public doomcom_t { - PRE_CONNECT, // Sent from guest to host for initial connection - PRE_KEEPALIVE, - PRE_DISCONNECT, // Sent from guest that aborts the game - PRE_ALLHERE, // Sent from host to guest when everybody has connected - PRE_CONACK, // Sent from host to guest to acknowledge PRE_CONNECT receipt - PRE_ALLFULL, // Sent from host to an unwanted guest - PRE_ALLHEREACK, // Sent from guest to host to acknowledge PRE_ALLHEREACK receipt - PRE_GO // Sent from host to guest to continue game startup +public: + DoomComImpl(int port); + ~DoomComImpl(); + + void PacketSend(const NetPacket &packet) override; + void PacketGet(NetPacket &packet) override; + + int Connect(const char *name) override; + void Close(int node) override; + +private: + void BuildAddress(sockaddr_in *address, const char *name); + int FindNode(const sockaddr_in *address); + + SOCKET mSocket = INVALID_SOCKET; + + sockaddr_in mNodeEndpoints[MAXNETNODES]; + uint64_t mNodeLastUpdate[MAXNETNODES]; + + uint8_t mTransmitBuffer[TRANSMIT_SIZE]; }; -// Set PreGamePacket.fake to this so that the game rejects any pregame packets -// after it starts. This translates to NCMD_SETUP|NCMD_MULTI. -#define PRE_FAKE 0x30 - -struct PreGamePacket +class InitSockets { - uint8_t Fake; - uint8_t Message; - uint8_t NumNodes; - union +public: + InitSockets() { - uint8_t ConsoleNum; - uint8_t NumPresent; - }; - struct - { - uint32_t address; - uint16_t port; - uint8_t player; - uint8_t pad; - } machines[MAXNETNODES]; -}; +#ifdef __WIN32__ + WSADATA wsad; -uint8_t TransmitBuffer[TRANSMIT_SIZE]; - -// -// UDPsocket -// -SOCKET UDPsocket (void) -{ - SOCKET s; - - // allocate a socket - s = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (s == INVALID_SOCKET) - I_FatalError ("can't create socket: %s", neterror ()); - - return s; -} - -// -// BindToLocalPort -// -void BindToLocalPort (SOCKET s, u_short port) -{ - int v; - sockaddr_in address; - - memset (&address, 0, sizeof(address)); - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(port); - - v = bind (s, (sockaddr *)&address, sizeof(address)); - if (v == SOCKET_ERROR) - I_FatalError ("BindToPort: %s", neterror ()); -} - -int FindNode (const sockaddr_in *address) -{ - int i; - - // find remote node number - for (i = 0; isin_addr.s_addr == sendaddress[i].sin_addr.s_addr - && address->sin_port == sendaddress[i].sin_port) - break; - - if (i == doomcom.numnodes) - { - // packet is not from one of the players (new game broadcast?) - i = -1; + if (WSAStartup(0x0101, &wsad)) + { + I_FatalError("Could not initialize Windows Sockets"); + } +#endif } - return i; + + ~InitSockets() + { +#ifdef __WIN32__ + WSACleanup(); +#endif + } +}; + +std::unique_ptr I_InitNetwork(int port) +{ + static InitSockets initsockets; + return std::unique_ptr(new DoomComImpl(port)); } -// -// PacketSend -// -void PacketSend (void) +DoomComImpl::DoomComImpl(int port) +{ + memset(mNodeEndpoints, 0, sizeof(mNodeEndpoints)); + memset(mNodeLastUpdate, 0, sizeof(mNodeLastUpdate)); + + mSocket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (mSocket == INVALID_SOCKET) + I_FatalError("can't create socket: %s", neterror()); + + if (port != 0) + { + sockaddr_in address; + memset(&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons(port); + + int v = bind(mSocket, (sockaddr *)&address, sizeof(address)); + if (v == SOCKET_ERROR) + I_FatalError("BindToPort: %s", neterror()); + } + +#ifndef __sun + u_long trueval = 1; + ioctlsocket(mSocket, FIONBIO, &trueval); +#else + u_long trueval = 1; + fcntl(mysocket, F_SETFL, trueval | O_NONBLOCK); +#endif +} + +DoomComImpl::~DoomComImpl() +{ + if (mSocket != INVALID_SOCKET) + { + closesocket(mSocket); + mSocket = INVALID_SOCKET; + } +} + +int DoomComImpl::Connect(const char *name) +{ + sockaddr_in addr; + BuildAddress(&addr, name); + return FindNode(&addr); +} + +void DoomComImpl::Close(int node) +{ + mNodeLastUpdate[node] = 0; +} + +int DoomComImpl::FindNode(const sockaddr_in *address) +{ + int slot = -1; + for (int i = 0; i < MAXNETNODES; i++) + { + if (mNodeLastUpdate[i] != 0 && address->sin_addr.s_addr == mNodeEndpoints[i].sin_addr.s_addr && address->sin_port == mNodeEndpoints[i].sin_port) + { + slot = i; + break; + } + else if (mNodeLastUpdate[i] == 0) + { + if (slot == -1) + slot = i; + } + } + + if (slot == -1) + return -1; + + mNodeEndpoints[slot] = *address; + mNodeLastUpdate[slot] = I_nsTime(); + return slot; +} + +void DoomComImpl::PacketSend(const NetPacket &packet) { int c; // FIXME: Catch this before we've overflown the buffer. With long chat // text and lots of backup tics, it could conceivably happen. (Though // apparently it hasn't yet, which is good.) - if (doomcom.datalength > MAX_MSGLEN) + if (packet.size > MAX_MSGLEN) { I_FatalError("Netbuffer overflow!"); } - assert(!(doomcom.data[0] & NCMD_COMPRESSED)); + assert(!(packet.data[0] & NCMD_COMPRESSED)); uLong size = TRANSMIT_SIZE - 1; - if (doomcom.datalength >= 10) + if (packet.size >= 10) { - TransmitBuffer[0] = doomcom.data[0] | NCMD_COMPRESSED; - c = compress2(TransmitBuffer + 1, &size, doomcom.data + 1, doomcom.datalength - 1, 9); + mTransmitBuffer[0] = packet.data[0] | NCMD_COMPRESSED; + c = compress2(mTransmitBuffer + 1, &size, packet.data + 1, packet.size - 1, 9); size += 1; } else { c = -1; // Just some random error code to avoid sending the compressed buffer. } - if (c == Z_OK && size < (uLong)doomcom.datalength) + if (c == Z_OK && size < (uLong)packet.size) { -// Printf("send %lu/%d\n", size, doomcom.datalength); - c = sendto(mysocket, (char *)TransmitBuffer, size, - 0, (sockaddr *)&sendaddress[doomcom.remotenode], - sizeof(sendaddress[doomcom.remotenode])); + c = sendto(mSocket, (char *)mTransmitBuffer, size, 0, (sockaddr *)&mNodeEndpoints[packet.node], sizeof(mNodeEndpoints[packet.node])); } else { - if (doomcom.datalength > TRANSMIT_SIZE) + if (packet.size > TRANSMIT_SIZE) { I_Error("Net compression failed (zlib error %d)", c); } else { -// Printf("send %d\n", doomcom.datalength); - c = sendto(mysocket, (char *)doomcom.data, doomcom.datalength, - 0, (sockaddr *)&sendaddress[doomcom.remotenode], - sizeof(sendaddress[doomcom.remotenode])); + c = sendto(mSocket, (char *)packet.data, packet.size, 0, (sockaddr *)&mNodeEndpoints[packet.node], sizeof(mNodeEndpoints[packet.node])); } } // if (c == -1) // I_Error ("SendPacket error: %s",strerror(errno)); } - -// -// PacketGet -// -void PacketGet (void) +void DoomComImpl::PacketGet(NetPacket &packet) { - int c; - socklen_t fromlen; - sockaddr_in fromaddress; - int node; - - fromlen = sizeof(fromaddress); - c = recvfrom (mysocket, (char*)TransmitBuffer, TRANSMIT_SIZE, 0, - (sockaddr *)&fromaddress, &fromlen); - node = FindNode (&fromaddress); - - if (node >= 0 && c == SOCKET_ERROR) + // First check if anything timed out. Treat this as a close. + uint64_t nowtime = I_nsTime(); + for (int i = 0; i < MAXNETNODES; i++) { - int err = WSAGetLastError(); - - if (err == WSAECONNRESET) - { // The remote node aborted unexpectedly, so pretend it sent an exit packet - - if (StartScreen != NULL) - { - StartScreen->NetMessage ("The connection from %s was dropped.\n", - players[sendplayer[node]].userinfo.GetName()); - } - else - { - Printf("The connection from %s was dropped.\n", - players[sendplayer[node]].userinfo.GetName()); - } - - doomcom.data[0] = 0x80; // NCMD_EXIT - c = 1; - } - else if (err != WSAEWOULDBLOCK) + if (mNodeLastUpdate[i] != 0 && nowtime - mNodeLastUpdate[i] > 5'000'000'000) // 5 second timeout { - I_Error ("GetPacket: %s", neterror ()); - } - else - { - doomcom.remotenode = -1; // no packet + Close(i); + packet.node = i; + packet.size = 0; return; } } - else if (node >= 0 && c > 0) + + while (true) { - doomcom.data[0] = TransmitBuffer[0] & ~NCMD_COMPRESSED; - if (TransmitBuffer[0] & NCMD_COMPRESSED) + sockaddr_in fromaddress; + socklen_t fromlen = sizeof(fromaddress); + int size = recvfrom(mSocket, (char*)mTransmitBuffer, TRANSMIT_SIZE, 0, (sockaddr *)&fromaddress, &fromlen); + if (size == SOCKET_ERROR) { - uLongf msgsize = MAX_MSGLEN - 1; - int err = uncompress(doomcom.data + 1, &msgsize, TransmitBuffer + 1, c - 1); -// Printf("recv %d/%lu\n", c, msgsize + 1); - if (err != Z_OK) + int err = WSAGetLastError(); + + if (err == WSAECONNRESET) // The remote node aborted unexpectedly. Treat this as a close. { - Printf("Net decompression failed (zlib error %s)\n", M_ZLibError(err).GetChars()); - // Pretend no packet - doomcom.remotenode = -1; + int node = FindNode(&fromaddress); + if (node == -1) + continue; + + Close(node); + packet.node = node; + packet.size = 0; + return; + } + else if (err != WSAEWOULDBLOCK) + { + I_Error("GetPacket: %s", neterror()); + } + else // no packet + { + packet.node = -1; + packet.size = 0; return; } - c = msgsize + 1; } - else + else if (size > 0) { -// Printf("recv %d\n", c); - memcpy(doomcom.data + 1, TransmitBuffer + 1, c - 1); + int node = FindNode(&fromaddress); + if (node == -1) + continue; + + packet.data[0] = mTransmitBuffer[0] & ~NCMD_COMPRESSED; + if (mTransmitBuffer[0] & NCMD_COMPRESSED) + { + uLongf msgsize = MAX_MSGLEN - 1; + int err = uncompress(packet.data + 1, &msgsize, mTransmitBuffer + 1, size - 1); + if (err != Z_OK) + { + Printf("Net decompression failed (zlib error %s)\n", M_ZLibError(err).GetChars()); + continue; + } + size = msgsize + 1; + } + else + { + memcpy(packet.data + 1, mTransmitBuffer + 1, size - 1); + } + + packet.node = node; + packet.size = (short)size; + return; } } - else if (c > 0) - { //The packet is not from any in-game node, so we might as well discard it. - // Don't show the message for disconnect notifications. - if (c != 2 || TransmitBuffer[0] != PRE_FAKE || TransmitBuffer[1] != PRE_DISCONNECT) - { - DPrintf(DMSG_WARNING, "Dropped packet: Unknown host (%s:%d)\n", inet_ntoa(fromaddress.sin_addr), fromaddress.sin_port); - } - doomcom.remotenode = -1; - return; - } - - doomcom.remotenode = node; - doomcom.datalength = (short)c; } -sockaddr_in *PreGet (void *buffer, int bufferlen, bool noabort) -{ - static sockaddr_in fromaddress; - socklen_t fromlen; - int c; - - fromlen = sizeof(fromaddress); - c = recvfrom (mysocket, (char *)buffer, bufferlen, 0, - (sockaddr *)&fromaddress, &fromlen); - - if (c == SOCKET_ERROR) - { - int err = WSAGetLastError(); - if (err == WSAEWOULDBLOCK || (noabort && err == WSAECONNRESET)) - return NULL; // no packet - I_Error ("PreGet: %s", neterror ()); - } - return &fromaddress; -} - -void PreSend (const void *buffer, int bufferlen, const sockaddr_in *to) -{ - sendto (mysocket, (const char *)buffer, bufferlen, 0, (const sockaddr *)to, sizeof(*to)); -} - -void BuildAddress (sockaddr_in *address, const char *name) +void DoomComImpl::BuildAddress(sockaddr_in *address, const char *name) { hostent *hostentry; // host information entry u_short port; @@ -366,13 +357,13 @@ void BuildAddress (sockaddr_in *address, const char *name) address->sin_family = AF_INET; - if ( (portpart = strchr (name, ':')) ) + if ((portpart = strchr(name, ':'))) { target = FString(name, portpart - name); - port = atoi (portpart + 1); + port = atoi(portpart + 1); if (!port) { - Printf ("Weird port: %s (using %d)\n", portpart + 1, DOOMPORT); + Printf("Weird port: %s (using %d)\n", portpart + 1, DOOMPORT); port = DOOMPORT; } } @@ -383,7 +374,7 @@ void BuildAddress (sockaddr_in *address, const char *name) } address->sin_port = htons(port); - for (curchar = 0; (c = target[curchar]) ; curchar++) + for (curchar = 0; (c = target[curchar]); curchar++) { if ((c < '0' || c > '9') && c != '.') { @@ -394,617 +385,19 @@ void BuildAddress (sockaddr_in *address, const char *name) if (!isnamed) { - address->sin_addr.s_addr = inet_addr (target); - Printf ("Node number %d, address %s\n", doomcom.numnodes, target.GetChars()); + address->sin_addr.s_addr = inet_addr(target); + // Printf("Node number %d, address %s\n", numnodes, target.GetChars()); } else { - hostentry = gethostbyname (target); + hostentry = gethostbyname(target); if (!hostentry) - I_FatalError ("gethostbyname: couldn't find %s\n%s", target.GetChars(), neterror()); + I_FatalError("gethostbyname: couldn't find %s\n%s", target.GetChars(), neterror()); address->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; - Printf ("Node number %d, hostname %s\n", - doomcom.numnodes, hostentry->h_name); + // Printf("Node number %d, hostname %s\n", numnodes, hostentry->h_name); } } -void CloseNetwork (void) -{ - if (mysocket != INVALID_SOCKET) - { - closesocket (mysocket); - mysocket = INVALID_SOCKET; - } -#ifdef __WIN32__ - WSACleanup (); -#endif -} - -void StartNetwork (bool autoPort) -{ - u_long trueval = 1; -#ifdef __WIN32__ - WSADATA wsad; - - if (WSAStartup (0x0101, &wsad)) - { - I_FatalError ("Could not initialize Windows Sockets"); - } -#endif - - atterm (CloseNetwork); - - netgame = true; - multiplayer = true; - - // create communication socket - mysocket = UDPsocket (); - BindToLocalPort (mysocket, autoPort ? 0 : DOOMPORT); -#ifndef __sun - ioctlsocket (mysocket, FIONBIO, &trueval); -#else - fcntl(mysocket, F_SETFL, trueval | O_NONBLOCK); -#endif -} - -void SendAbort (void) -{ - uint8_t dis[2] = { PRE_FAKE, PRE_DISCONNECT }; - int i, j; - - if (doomcom.numnodes > 1) - { - if (consoleplayer == 0) - { - // The host needs to let everyone know - for (i = 1; i < doomcom.numnodes; ++i) - { - for (j = 4; j > 0; --j) - { - PreSend (dis, 2, &sendaddress[i]); - } - } - } - else - { - // Guests only need to let the host know. - for (i = 4; i > 0; --i) - { - PreSend (dis, 2, &sendaddress[1]); - } - } - } -} - -static void SendConAck (int num_connected, int num_needed) -{ - PreGamePacket packet; - - packet.Fake = PRE_FAKE; - packet.Message = PRE_CONACK; - packet.NumNodes = num_needed; - packet.NumPresent = num_connected; - for (int node = 1; node < doomcom.numnodes; ++node) - { - PreSend (&packet, 4, &sendaddress[node]); - } - StartScreen->NetProgress (doomcom.numnodes); -} - -bool Host_CheckForConnects (void *userdata) -{ - PreGamePacket packet; - int numplayers = (int)(intptr_t)userdata; - sockaddr_in *from; - int node; - - while ( (from = PreGet (&packet, sizeof(packet), false)) ) - { - if (packet.Fake != PRE_FAKE) - { - continue; - } - switch (packet.Message) - { - case PRE_CONNECT: - node = FindNode (from); - if (doomcom.numnodes == numplayers) - { - if (node == -1) - { - const uint8_t *s_addr_bytes = (const uint8_t *)&from->sin_addr; - StartScreen->NetMessage ("Got extra connect from %d.%d.%d.%d:%d", - s_addr_bytes[0], s_addr_bytes[1], s_addr_bytes[2], s_addr_bytes[3], - from->sin_port); - packet.Message = PRE_ALLFULL; - PreSend (&packet, 2, from); - } - } - else - { - if (node == -1) - { - node = doomcom.numnodes++; - sendaddress[node] = *from; - StartScreen->NetMessage ("Got connect from node %d.", node); - } - - // Let the new guest (and everyone else) know we got their message. - SendConAck (doomcom.numnodes, numplayers); - } - break; - - case PRE_DISCONNECT: - node = FindNode (from); - if (node >= 0) - { - StartScreen->NetMessage ("Got disconnect from node %d.", node); - doomcom.numnodes--; - while (node < doomcom.numnodes) - { - sendaddress[node] = sendaddress[node+1]; - node++; - } - - // Let remaining guests know that somebody left. - SendConAck (doomcom.numnodes, numplayers); - } - break; - - case PRE_KEEPALIVE: - break; - } - } - if (doomcom.numnodes < numplayers) - { - // Send message to everyone as a keepalive - SendConAck(doomcom.numnodes, numplayers); - return false; - } - - // It's possible somebody bailed out after all players were found. - // Unfortunately, this isn't guaranteed to catch all of them. - // Oh well. Better than nothing. - while ( (from = PreGet (&packet, sizeof(packet), false)) ) - { - if (packet.Fake == PRE_FAKE && packet.Message == PRE_DISCONNECT) - { - node = FindNode (from); - if (node >= 0) - { - doomcom.numnodes--; - while (node < doomcom.numnodes) - { - sendaddress[node] = sendaddress[node+1]; - node++; - } - // Let remaining guests know that somebody left. - SendConAck (doomcom.numnodes, numplayers); - } - break; - } - } - return doomcom.numnodes >= numplayers; -} - -bool Host_SendAllHere (void *userdata) -{ - int *gotack = (int *)userdata; // ackcount is at gotack[MAXNETNODES] - PreGamePacket packet; - int node; - sockaddr_in *from; - - // Send out address information to all guests. Guests that have already - // acknowledged receipt effectively get just a heartbeat packet. - packet.Fake = PRE_FAKE; - packet.Message = PRE_ALLHERE; - for (node = 1; node < doomcom.numnodes; node++) - { - int machine, spot = 0; - - packet.ConsoleNum = node; - if (!gotack[node]) - { - for (spot = 0, machine = 1; machine < doomcom.numnodes; machine++) - { - if (node != machine) - { - packet.machines[spot].address = sendaddress[machine].sin_addr.s_addr; - packet.machines[spot].port = sendaddress[machine].sin_port; - packet.machines[spot].player = node; - - spot++; // fixes problem of new address replacing existing address in - // array; it's supposed to increment the index before getting - // and storing in the packet the next address. - } - } - packet.NumNodes = doomcom.numnodes - 2; - } - else - { - packet.NumNodes = 0; - } - PreSend (&packet, 4 + spot*8, &sendaddress[node]); - } - - // Check for replies. - while ( (from = PreGet (&packet, sizeof(packet), false)) ) - { - if (packet.Fake == PRE_FAKE && packet.Message == PRE_ALLHEREACK) - { - node = FindNode (from); - if (node >= 0) - { - if (!gotack[node]) - { - gotack[node] = true; - gotack[MAXNETNODES]++; - } - } - PreSend (&packet, 2, from); - } - } - - // If everybody has replied, then this loop can end. - return gotack[MAXNETNODES] == doomcom.numnodes - 1; -} - -void HostGame (int i) -{ - PreGamePacket packet; - int numplayers; - int node; - int gotack[MAXNETNODES+1]; - - if ((i == Args->NumArgs() - 1) || !(numplayers = atoi (Args->GetArg(i+1)))) - { // No player count specified, assume 2 - numplayers = 2; - } - - if (numplayers > MAXNETNODES) - { - I_FatalError("You cannot host a game with %d players. The limit is currently %d.", numplayers, MAXNETNODES); - return; - } - - if (numplayers == 1) - { // Special case: Only 1 player, so don't bother starting the network - netgame = false; - multiplayer = true; - doomcom.id = DOOMCOM_ID; - doomcom.numplayers = doomcom.numnodes = 1; - doomcom.consoleplayer = 0; - return; - } - - StartNetwork (false); - - // [JC] - this computer is starting the game, therefore it should - // be the Net Arbitrator. - doomcom.consoleplayer = 0; - Printf ("Console player number: %d\n", doomcom.consoleplayer); - - doomcom.numnodes = 1; - - atterm (SendAbort); - - StartScreen->NetInit ("Waiting for players", numplayers); - - // Wait for numplayers-1 different connections - if (!StartScreen->NetLoop (Host_CheckForConnects, (void *)(intptr_t)numplayers)) - { - exit (0); - } - - // Now inform everyone of all machines involved in the game - memset (gotack, 0, sizeof(gotack)); - StartScreen->NetMessage ("Sending all here."); - StartScreen->NetInit ("Done waiting", 1); - - if (!StartScreen->NetLoop (Host_SendAllHere, (void *)gotack)) - { - exit (0); - } - - popterm (); - - // Now go - StartScreen->NetMessage ("Go"); - packet.Fake = PRE_FAKE; - packet.Message = PRE_GO; - for (node = 1; node < doomcom.numnodes; node++) - { - // If we send the packets eight times to each guest, - // hopefully at least one of them will get through. - for (int i = 8; i != 0; --i) - { - PreSend (&packet, 2, &sendaddress[node]); - } - } - - StartScreen->NetMessage ("Total players: %d", doomcom.numnodes); - - doomcom.id = DOOMCOM_ID; - doomcom.numplayers = doomcom.numnodes; - - // On the host, each player's number is the same as its node number - for (i = 0; i < doomcom.numnodes; ++i) - { - sendplayer[i] = i; - } -} - -// This routine is used by a guest to notify the host of its presence. -// Once that host acknowledges receipt of the notification, this routine -// is never called again. - -bool Guest_ContactHost (void *userdata) -{ - sockaddr_in *from; - PreGamePacket packet; - - // Let the host know we are here. - packet.Fake = PRE_FAKE; - packet.Message = PRE_CONNECT; - PreSend (&packet, 2, &sendaddress[1]); - - // Listen for a reply. - while ( (from = PreGet (&packet, sizeof(packet), true)) ) - { - if (packet.Fake == PRE_FAKE && FindNode(from) == 1) - { - if (packet.Message == PRE_CONACK) - { - StartScreen->NetMessage ("Total players: %d", packet.NumNodes); - StartScreen->NetInit ("Waiting for other players", packet.NumNodes); - StartScreen->NetProgress (packet.NumPresent); - return true; - } - else if (packet.Message == PRE_DISCONNECT) - { - doomcom.numnodes = 0; - I_FatalError ("The host cancelled the game."); - } - else if (packet.Message == PRE_ALLFULL) - { - doomcom.numnodes = 0; - I_FatalError ("The game is full."); - } - } - } - - // In case the progress bar could not be marqueed, bump it. - StartScreen->NetProgress (0); - - return false; -} - -bool Guest_WaitForOthers (void *userdata) -{ - sockaddr_in *from; - PreGamePacket packet; - - while ( (from = PreGet (&packet, sizeof(packet), false)) ) - { - if (packet.Fake != PRE_FAKE || FindNode(from) != 1) - { - continue; - } - switch (packet.Message) - { - case PRE_CONACK: - StartScreen->NetProgress (packet.NumPresent); - break; - - case PRE_ALLHERE: - if (doomcom.numnodes == 2) - { - int node; - - doomcom.numnodes = packet.NumNodes + 2; - sendplayer[0] = packet.ConsoleNum; // My player number - doomcom.consoleplayer = packet.ConsoleNum; - StartScreen->NetMessage ("Console player number: %d", doomcom.consoleplayer); - for (node = 0; node < packet.NumNodes; node++) - { - sendaddress[node+2].sin_addr.s_addr = packet.machines[node].address; - sendaddress[node+2].sin_port = packet.machines[node].port; - sendplayer[node+2] = packet.machines[node].player; - - // [JC] - fixes problem of games not starting due to - // no address family being assigned to nodes stored in - // sendaddress[] from the All Here packet. - sendaddress[node+2].sin_family = AF_INET; - } - } - - StartScreen->NetMessage ("Received All Here, sending ACK."); - packet.Fake = PRE_FAKE; - packet.Message = PRE_ALLHEREACK; - PreSend (&packet, 2, &sendaddress[1]); - break; - - case PRE_GO: - StartScreen->NetMessage ("Received \"Go.\""); - return true; - - case PRE_DISCONNECT: - I_FatalError ("The host cancelled the game."); - break; - } - } - - packet.Fake = PRE_FAKE; - packet.Message = PRE_KEEPALIVE; - PreSend(&packet, 2, &sendaddress[1]); - - return false; -} - -void JoinGame (int i) -{ - if ((i == Args->NumArgs() - 1) || - (Args->GetArg(i+1)[0] == '-') || - (Args->GetArg(i+1)[0] == '+')) - I_FatalError ("You need to specify the host machine's address"); - - StartNetwork (true); - - // Host is always node 1 - BuildAddress (&sendaddress[1], Args->GetArg(i+1)); - sendplayer[1] = 0; - doomcom.numnodes = 2; - - atterm (SendAbort); - - // Let host know we are here - StartScreen->NetInit ("Contacting host", 0); - - if (!StartScreen->NetLoop (Guest_ContactHost, NULL)) - { - exit (0); - } - - // Wait for everyone else to connect - if (!StartScreen->NetLoop (Guest_WaitForOthers, 0)) - { - exit (0); - } - - popterm (); - - StartScreen->NetMessage ("Total players: %d", doomcom.numnodes); - - doomcom.id = DOOMCOM_ID; - doomcom.numplayers = doomcom.numnodes; -} - -static int PrivateNetOf(in_addr in) -{ - int addr = ntohl(in.s_addr); - if ((addr & 0xFFFF0000) == 0xC0A80000) // 192.168.0.0 - { - return 0xC0A80000; - } - else if ((addr & 0xFFF00000) == 0xAC100000) // 172.16.0.0 - { - return 0xAC100000; - } - else if ((addr & 0xFF000000) == 0x0A000000) // 10.0.0.0 - { - return 0x0A000000; - } - else if ((addr & 0xFF000000) == 0x7F000000) // 127.0.0.0 (localhost) - { - return 0x7F000000; - } - // Not a private IP - return 0; -} - -// -// NodesOnSameNetwork -// -// The best I can really do here is check if the others are on the same -// private network, since that means we (probably) are too. -// - -static bool NodesOnSameNetwork() -{ - int net1; - - net1 = PrivateNetOf(sendaddress[1].sin_addr); -// Printf("net1 = %08x\n", net1); - if (net1 == 0) - { - return false; - } - for (int i = 2; i < doomcom.numnodes; ++i) - { - int net = PrivateNetOf(sendaddress[i].sin_addr); -// Printf("Net[%d] = %08x\n", i, net); - if (net != net1) - { - return false; - } - } - return true; -} - -// -// I_InitNetwork -// -// Returns true if packet server mode might be a good idea. -// -bool I_InitNetwork (void) -{ - int i; - const char *v; - - memset (&doomcom, 0, sizeof(doomcom)); - - // set up for network - v = Args->CheckValue ("-dup"); - if (v) - { - doomcom.ticdup = clamp (atoi (v), 1, MAXTICDUP); - } - else - { - doomcom.ticdup = 1; - } - - v = Args->CheckValue ("-port"); - if (v) - { - DOOMPORT = atoi (v); - Printf ("using alternate port %i\n", DOOMPORT); - } - - // parse network game options, - // player 1: -host - // player x: -join - if ( (i = Args->CheckParm ("-host")) ) - { - HostGame (i); - } - else if ( (i = Args->CheckParm ("-join")) ) - { - JoinGame (i); - } - else - { - // single player game - netgame = false; - multiplayer = false; - doomcom.id = DOOMCOM_ID; - doomcom.numplayers = doomcom.numnodes = 1; - doomcom.consoleplayer = 0; - return false; - } - if (doomcom.numnodes < 3) - { // Packet server mode with only two players is effectively the same as - // peer-to-peer but with some slightly larger packets. - return false; - } - return doomcom.numnodes > 3 || !NodesOnSameNetwork(); -} - - -void I_NetCmd (void) -{ - if (doomcom.command == CMD_SEND) - { - PacketSend (); - } - else if (doomcom.command == CMD_GET) - { - PacketGet (); - } - else - I_Error ("Bad net cmd: %i\n",doomcom.command); -} - #ifdef __WIN32__ const char *neterror (void) { diff --git a/src/i_net.h b/src/i_net.h index 124116cf5c..ccea62bacb 100644 --- a/src/i_net.h +++ b/src/i_net.h @@ -1,8 +1,38 @@ -#ifndef __I_NET_H__ -#define __I_NET_H__ -// Called by D_DoomMain. -bool I_InitNetwork (void); -void I_NetCmd (void); +#pragma once -#endif +#include + +#define MAX_MSGLEN 14000 +#define DOOMPORT 5029 + +struct NetPacket +{ + NetPacket() { memset(data, 0, sizeof(data)); } + + // packet data to be sent + uint8_t data[MAX_MSGLEN]; + + // bytes in data to be sent + int16_t size = 0; + + // dest for send, set by get (-1 = no packet). + int16_t node = 0; + + uint8_t &operator[](int i) { return data[i]; } + const uint8_t &operator[](int i) const { return data[i]; } +}; + +// Network packet data. +struct doomcom_t +{ + virtual ~doomcom_t() { } + + virtual void PacketSend(const NetPacket &packet) = 0; + virtual void PacketGet(NetPacket &packet) = 0; + + virtual int Connect(const char *name) = 0; + virtual void Close(int node) = 0; +}; + +std::unique_ptr I_InitNetwork(int port); diff --git a/src/info.cpp b/src/info.cpp index dd87e85f60..185de36efc 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -587,17 +587,17 @@ static void SummonActor (int command, int command2, FCommandLine argv) Printf ("Unknown actor '%s'\n", argv[1]); return; } - Net_WriteByte (argv.argc() > 2 ? command2 : command); - Net_WriteString (type->TypeName.GetChars()); + network->WriteByte (argv.argc() > 2 ? command2 : command); + network->WriteString (type->TypeName.GetChars()); if (argv.argc () > 2) { - Net_WriteWord (atoi (argv[2])); // angle - Net_WriteWord ((argv.argc() > 3) ? atoi(argv[3]) : 0); // TID - Net_WriteByte ((argv.argc() > 4) ? atoi(argv[4]) : 0); // special + network->WriteWord (atoi (argv[2])); // angle + network->WriteWord ((argv.argc() > 3) ? atoi(argv[3]) : 0); // TID + network->WriteByte ((argv.argc() > 4) ? atoi(argv[4]) : 0); // special for (int i = 5; i < 10; i++) { // args[5] - Net_WriteLong((i < argv.argc()) ? atoi(argv[i]) : 0); + network->WriteLong((i < argv.argc()) ? atoi(argv[i]) : 0); } } } diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index 30ef3fa400..66c2b975fc 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -761,7 +761,7 @@ bool DIntermissionController::Responder (event_t *ev) int res = mScreen->Responder(ev); if (res == -1 && !mSentAdvance) { - Net_WriteByte(DEM_ADVANCEINTER); + network->WriteByte(DEM_ADVANCEINTER); mSentAdvance = true; } return !!res; diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 768802693e..697f20be1e 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -689,6 +689,6 @@ CCMD (mdk) return; const char *name = argv.argc() > 1 ? argv[1] : ""; - Net_WriteByte (DEM_MDK); - Net_WriteString(name); + network->WriteByte (DEM_MDK); + network->WriteString(name); } diff --git a/src/m_random.cpp b/src/m_random.cpp index 5b77bccaf3..559d00f28f 100644 --- a/src/m_random.cpp +++ b/src/m_random.cpp @@ -267,7 +267,7 @@ void FRandom::Init(uint32_t seed) // // FRandom :: StaticSumSeeds // -// This function produces a uint32_t that can be used to check the consistancy +// This function produces a uint32_t that can be used to check the consistency // of network games between different machines. Only a select few RNGs are // used for the sum, because not all RNGs are important to network sync. // diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index cce572976f..ff4f554b7a 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -695,17 +695,17 @@ DEFINE_ACTION_FUNCTION(DConversationMenu, SendConversationReply) switch (node) { case -1: - Net_WriteByte(DEM_CONVNULL); + network->WriteByte(DEM_CONVNULL); break; case -2: - Net_WriteByte(DEM_CONVCLOSE); + network->WriteByte(DEM_CONVCLOSE); break; default: - Net_WriteByte(DEM_CONVREPLY); - Net_WriteWord(node); - Net_WriteByte(reply); + network->WriteByte(DEM_CONVREPLY); + network->WriteWord(node); + network->WriteByte(reply); break; } StaticLastReply = reply; diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index db1578d494..c97ee754ac 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -1987,8 +1987,8 @@ CCMD (kill) if (CheckCheatmode ()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_MASSACRE); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_MASSACRE); } else if (!stricmp (argv[1], "baddies")) { @@ -1996,13 +1996,13 @@ CCMD (kill) if (CheckCheatmode ()) return; - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (CHT_MASSACRE2); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (CHT_MASSACRE2); } else { - Net_WriteByte (DEM_KILLCLASSCHEAT); - Net_WriteString (argv[1]); + network->WriteByte (DEM_KILLCLASSCHEAT); + network->WriteString (argv[1]); } } else @@ -2012,7 +2012,7 @@ CCMD (kill) return; // Kill the player - Net_WriteByte (DEM_SUICIDE); + network->WriteByte (DEM_SUICIDE); } C_HideConsole (); } @@ -2024,8 +2024,8 @@ CCMD(remove) if (CheckCheatmode()) return; - Net_WriteByte(DEM_REMOVE); - Net_WriteString(argv[1]); + network->WriteByte(DEM_REMOVE); + network->WriteString(argv[1]); C_HideConsole(); } else @@ -2033,5 +2033,4 @@ CCMD(remove) Printf("Usage: remove \n"); return; } - } diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 253f505e42..d3a1402a4e 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -3130,7 +3130,7 @@ FUNC(LS_Autosave) if (gameaction != ga_savegame) { level.flags2 &= ~LEVEL2_NOAUTOSAVEHINT; - Net_WriteByte (DEM_CHECKAUTOSAVE); + network->WriteByte (DEM_CHECKAUTOSAVE); } return true; } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index dba5a109e3..f014e6488c 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -1838,6 +1838,10 @@ void P_SpawnThings (int position) for (int i=0; i < numthings; i++) { + // Only spawn the player mobj for the client in a client/server game + if (netclient && (MapThingsConverted[i].info->Type || MapThingsConverted[i].info->Special != SMT_Player1Start + position)) + continue; + AActor *actor = SpawnMapThing (i, &MapThingsConverted[i], position); unsigned *udi = MapThingsUserDataIndex.CheckKey((unsigned)i); if (udi != NULL) diff --git a/src/p_user.cpp b/src/p_user.cpp index cb6c370cdd..8ae754ceba 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -592,7 +592,7 @@ void player_t::SetFOV(float fov) { if (consoleplayer == Net_Arbitrator) { - Net_WriteByte(DEM_MYFOV); + network->WriteByte(DEM_MYFOV); } else { @@ -602,9 +602,9 @@ void player_t::SetFOV(float fov) } else { - Net_WriteByte(DEM_MYFOV); + network->WriteByte(DEM_MYFOV); } - Net_WriteFloat(clamp(fov, 5.f, 179.f)); + network->WriteFloat(clamp(fov, 5.f, 179.f)); } } @@ -747,9 +747,9 @@ void player_t::SendPitchLimits() const uppitch = downpitch = (int)maxviewpitch; } - Net_WriteByte(DEM_SETPITCHLIMIT); - Net_WriteByte(uppitch); - Net_WriteByte(downpitch); + network->WriteByte(DEM_SETPITCHLIMIT); + network->WriteByte(uppitch); + network->WriteByte(downpitch); } } @@ -1038,7 +1038,7 @@ void APlayerPawn::PostBeginPlay() // // Sets up the default weapon slots for this player. If this is also the // local player, determines local modifications and sends those across the -// network. Ignores voodoo dolls. +// network-> Ignores voodoo dolls. // //=========================================================================== @@ -2451,8 +2451,6 @@ nodetype *RestoreNodeList(AActor *act, nodetype *head, nodetype *linktype::*othe void P_PredictPlayer (player_t *player) { - int maxtic; - if (cl_noprediction || singletics || demoplayback || @@ -2466,7 +2464,7 @@ void P_PredictPlayer (player_t *player) return; } - maxtic = maketic; + int maxtic = network->GetSendTick(); if (gametic == maxtic) { @@ -2517,13 +2515,13 @@ void P_PredictPlayer (player_t *player) act->BlockNode = NULL; // Values too small to be usable for lerping can be considered "off". - bool CanLerp = (!(cl_predict_lerpscale < 0.01f) && (ticdup == 1)), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); + bool CanLerp = (!(cl_predict_lerpscale < 0.01f) && (network->ticdup == 1)), DoLerp = false, NoInterpolateOld = R_GetViewInterpolationStatus(); for (int i = gametic; i < maxtic; ++i) { if (!NoInterpolateOld) R_RebuildViewInterpolation(player); - player->cmd = localcmds[i % LOCALCMDTICS]; + player->cmd = network->GetSentInput(i); P_PlayerThink (player); player->mo->Tick (); @@ -2590,6 +2588,7 @@ void P_UnPredictPlayer () int inventorytics = player->inventorytics; *player = PredictionPlayerBackup; + PredictionPlayerBackup = {}; // Clear the copy so its destructor doesn't try delete the psprites list // Restore the camera instead of using the backup's copy, because spynext/prev // could cause it to change during prediction. diff --git a/src/polyrenderer/poly_renderer.cpp b/src/polyrenderer/poly_renderer.cpp index 76d8a937e1..e28b8f47ee 100644 --- a/src/polyrenderer/poly_renderer.cpp +++ b/src/polyrenderer/poly_renderer.cpp @@ -119,8 +119,6 @@ void PolyRenderer::RenderActorView(AActor *actor, bool dontmaplines) PolyMaskedCycles.Reset(); PolyDrawerWaitCycles.Reset(); - NetUpdate(); - DontMapLines = dontmaplines; R_SetupFrame(Viewpoint, Viewwindow, actor); @@ -169,8 +167,6 @@ void PolyRenderer::RenderActorView(AActor *actor, bool dontmaplines) if (Viewpoint.camera) Viewpoint.camera->renderflags = savedflags; interpolator.RestoreInterpolations (); - - NetUpdate(); } void PolyRenderer::RenderRemainingPlayerSprites() diff --git a/src/posix/cocoa/i_system.mm b/src/posix/cocoa/i_system.mm index 94a911bfa2..7c0e2e95e8 100644 --- a/src/posix/cocoa/i_system.mm +++ b/src/posix/cocoa/i_system.mm @@ -54,20 +54,10 @@ EXTERN_CVAR(String, language) uint32_t LanguageIDs[4]; - void I_Tactile(int /*on*/, int /*off*/, int /*total*/) { } - -ticcmd_t* I_BaseTiccmd() -{ - static ticcmd_t emptycmd; - return &emptycmd; -} - - - // // SetLanguageIDs // diff --git a/src/posix/cocoa/st_start.mm b/src/posix/cocoa/st_start.mm index d10722ffb2..e5d47ebe3f 100644 --- a/src/posix/cocoa/st_start.mm +++ b/src/posix/cocoa/st_start.mm @@ -71,7 +71,7 @@ public: virtual void NetProgress(int count); virtual void NetMessage(const char *format, ...); virtual void NetDone(); - virtual bool NetLoop(bool (*timerCallback)(void*), void* userData); + virtual bool NetLoop(std::function callback); }; @@ -153,11 +153,11 @@ void FBasicStartupScreen::NetDone() FConsoleWindow::GetInstance().NetDone(); } -bool FBasicStartupScreen::NetLoop(bool (*timerCallback)(void*), void* const userData) +bool FBasicStartupScreen::NetLoop(std::function callback) { while (true) { - if (timerCallback(userData)) + if (callback()) { break; } diff --git a/src/posix/i_system.h b/src/posix/i_system.h index 7f468f1432..3bcb7b70e6 100644 --- a/src/posix/i_system.h +++ b/src/posix/i_system.h @@ -79,17 +79,6 @@ void I_StartFrame (void); // Can call D_PostEvent. void I_StartTic (void); -// Asynchronous interrupt functions should maintain private queues -// that are read by the synchronous functions -// to be converted into events. - -// Either returns a null ticcmd, -// or calls a loadable driver to build it. -// This ticcmd will then be modified by the gameloop -// for normal input. -ticcmd_t *I_BaseTiccmd (void); - - // Called by M_Responder when quit is selected. // Clean exit, displays sell blurb. void I_Quit (void); diff --git a/src/posix/sdl/i_system.cpp b/src/posix/sdl/i_system.cpp index 173f8a12f5..e1dfdbee0a 100644 --- a/src/posix/sdl/i_system.cpp +++ b/src/posix/sdl/i_system.cpp @@ -75,12 +75,6 @@ void I_Tactile (int /*on*/, int /*off*/, int /*total*/) { } -ticcmd_t emptycmd; -ticcmd_t *I_BaseTiccmd(void) -{ - return &emptycmd; -} - void I_BeginRead(void) { } diff --git a/src/posix/sdl/st_start.cpp b/src/posix/sdl/st_start.cpp index 38959f542d..c4d027866c 100644 --- a/src/posix/sdl/st_start.cpp +++ b/src/posix/sdl/st_start.cpp @@ -58,7 +58,7 @@ class FTTYStartupScreen : public FStartupScreen void NetProgress(int count); void NetMessage(const char *format, ...); // cover for printf void NetDone(); - bool NetLoop(bool (*timer_callback)(void *), void *userdata); + bool NetLoop(std::function callback); protected: bool DidNetInit; int NetMaxPos, NetCurPos; @@ -304,7 +304,7 @@ void FTTYStartupScreen::NetProgress(int count) // //=========================================================================== -bool FTTYStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata) +bool FTTYStartupScreen::NetLoop(std::function callback) { fd_set rfds; struct timeval tv; @@ -328,7 +328,7 @@ bool FTTYStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata) } else if (retval == 0) { - if (timer_callback (userdata)) + if (callback()) { fputc ('\n', stderr); return true; diff --git a/src/st_start.h b/src/st_start.h index a55b471977..f542b4af51 100644 --- a/src/st_start.h +++ b/src/st_start.h @@ -35,6 +35,8 @@ ** Actual implementation is system-specific. */ +#include + class FStartupScreen { public: @@ -51,7 +53,7 @@ public: virtual void NetProgress(int count); virtual void NetMessage(const char *format, ...); // cover for printf virtual void NetDone(); - virtual bool NetLoop(bool (*timer_callback)(void *), void *userdata); + virtual bool NetLoop(std::function callback); protected: int MaxPos, CurPos, NotchPos; }; diff --git a/src/st_stuff.cpp b/src/st_stuff.cpp index fe96d62655..366453ade8 100644 --- a/src/st_stuff.cpp +++ b/src/st_stuff.cpp @@ -433,8 +433,8 @@ static bool CheatAddKey (cheatseq_t *cheat, uint8_t key, bool *eat) static bool Cht_Generic (cheatseq_t *cheat) { - Net_WriteByte (DEM_GENERICCHEAT); - Net_WriteByte (cheat->Args[0]); + network->WriteByte (DEM_GENERICCHEAT); + network->WriteByte (cheat->Args[0]); return true; } diff --git a/src/statistics.cpp b/src/statistics.cpp index 0a3aeadeaa..d4169ef314 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -584,7 +584,7 @@ CCMD(finishgame) return; } // This CCMD simulates an end-of-game action and exists to end mods that never exit their last level. - Net_WriteByte(DEM_FINISHGAME); + network->WriteByte(DEM_FINISHGAME); } ADD_STAT(statistics) diff --git a/src/swrenderer/line/r_renderdrawsegment.cpp b/src/swrenderer/line/r_renderdrawsegment.cpp index f21c9e79f3..7ebb8d99d8 100644 --- a/src/swrenderer/line/r_renderdrawsegment.cpp +++ b/src/swrenderer/line/r_renderdrawsegment.cpp @@ -85,9 +85,6 @@ namespace swrenderer return; } - if (Thread->MainThread) - NetUpdate(); - frontsector = curline->frontsector; backsector = curline->backsector; diff --git a/src/swrenderer/line/r_walldraw.cpp b/src/swrenderer/line/r_walldraw.cpp index 64f14135f4..0437f5d83a 100644 --- a/src/swrenderer/line/r_walldraw.cpp +++ b/src/swrenderer/line/r_walldraw.cpp @@ -373,9 +373,6 @@ namespace swrenderer WallSampler sampler(Thread->Viewport.get(), y1, texturemid, swal[x], yrepeat, lwal[x] + xoffset, xmagnitude, rw_pic); Draw1Column(x, y1, y2, sampler); } - - if (Thread->MainThread) - NetUpdate(); } void RenderWallPart::ProcessNormalWall(const short *uwal, const short *dwal, double texturemid, float *swal, fixed_t *lwal) diff --git a/src/swrenderer/plane/r_visibleplane.cpp b/src/swrenderer/plane/r_visibleplane.cpp index ac4e070796..19cb0fb42d 100644 --- a/src/swrenderer/plane/r_visibleplane.cpp +++ b/src/swrenderer/plane/r_visibleplane.cpp @@ -142,7 +142,5 @@ namespace swrenderer renderer.Render(this, xscale, yscale, alpha, additive, masked, colormap, tex); } } - if (thread->MainThread) - NetUpdate(); } } diff --git a/src/swrenderer/scene/r_portal.cpp b/src/swrenderer/scene/r_portal.cpp index 46e6e83574..5de670dc88 100644 --- a/src/swrenderer/scene/r_portal.cpp +++ b/src/swrenderer/scene/r_portal.cpp @@ -455,14 +455,8 @@ namespace swrenderer int prevuniq2 = CurrentPortalUniq; CurrentPortalUniq = prevuniq; - if (Thread->MainThread) - NetUpdate(); - Thread->TranslucentPass->Render(); // this is required since with portals there often will be cases when more than 80% of the view is inside a portal. - if (Thread->MainThread) - NetUpdate(); - Thread->Clip3D->LeaveSkybox(); // pop 3D floor height map CurrentPortalUniq = prevuniq2; diff --git a/src/swrenderer/scene/r_scene.cpp b/src/swrenderer/scene/r_scene.cpp index d51eef63a8..27e52bf9ed 100644 --- a/src/swrenderer/scene/r_scene.cpp +++ b/src/swrenderer/scene/r_scene.cpp @@ -144,8 +144,6 @@ namespace swrenderer CameraLight::Instance()->SetCamera(MainThread()->Viewport->viewpoint, MainThread()->Viewport->RenderTarget, actor); MainThread()->Viewport->SetupFreelook(); - NetUpdate(); - this->dontmaplines = dontmaplines; // [RH] Setup particles for this frame @@ -271,9 +269,6 @@ namespace swrenderer thread->OpaquePass->RenderScene(); thread->Clip3D->ResetClip(); // reset clips (floor/ceiling) - if (thread->MainThread) - NetUpdate(); - if (viewactive) { thread->PlaneList->Render(); @@ -281,13 +276,7 @@ namespace swrenderer thread->Portal->RenderPlanePortals(); thread->Portal->RenderLinePortals(); - if (thread->MainThread) - NetUpdate(); - thread->TranslucentPass->Render(); - - if (thread->MainThread) - NetUpdate(); } DrawerThreads::Execute(thread->DrawQueue); diff --git a/src/swrenderer/things/r_playersprite.cpp b/src/swrenderer/things/r_playersprite.cpp index e1156f4fd4..580bb293e3 100644 --- a/src/swrenderer/things/r_playersprite.cpp +++ b/src/swrenderer/things/r_playersprite.cpp @@ -541,8 +541,5 @@ namespace swrenderer drawerargs.DrawMaskedColumn(thread, x, iscale, pic, frac + xiscale / 2, spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip, RenderStyle, false); frac += xiscale; } - - if (thread->MainThread) - NetUpdate(); } } diff --git a/src/swrenderer/things/r_sprite.cpp b/src/swrenderer/things/r_sprite.cpp index 36e9ca89f3..4984da466a 100644 --- a/src/swrenderer/things/r_sprite.cpp +++ b/src/swrenderer/things/r_sprite.cpp @@ -371,8 +371,5 @@ namespace swrenderer } } } - - if (thread->MainThread) - NetUpdate(); } } diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 2a49a2426f..1aef2b07a6 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -142,7 +142,6 @@ int sys_ostype = 0; // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static ticcmd_t emptycmd; static bool HasExited; static WadStuff *WadList; @@ -166,20 +165,6 @@ void I_Tactile(int on, int off, int total) on = off = total = 0; } -//========================================================================== -// -// I_BaseTiccmd -// -// Returns an empty ticcmd. I have no idea why this should be system- -// specific. -// -//========================================================================== - -ticcmd_t *I_BaseTiccmd() -{ - return &emptycmd; -} - //========================================================================== // // I_DetectOS diff --git a/src/win32/i_system.h b/src/win32/i_system.h index a5f7b5d0ba..b070364122 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -72,16 +72,6 @@ void I_StartFrame (void); // Can call D_PostEvent. void I_StartTic (void); -// Asynchronous interrupt functions should maintain private queues -// that are read by the synchronous functions -// to be converted into events. - -// Either returns a null ticcmd, -// or calls a loadable driver to build it. -// This ticcmd will then be modified by the gameloop -// for normal input. -ticcmd_t *I_BaseTiccmd (void); - // Called by M_Responder when quit is selected. // Clean exit, displays sell blurb. diff --git a/src/win32/st_start.cpp b/src/win32/st_start.cpp index 549ccad1ce..13e70e2acf 100644 --- a/src/win32/st_start.cpp +++ b/src/win32/st_start.cpp @@ -121,7 +121,7 @@ public: void NetProgress(int count); void NetMessage(const char *format, ...); // cover for printf void NetDone(); - bool NetLoop(bool (*timer_callback)(void *), void *userdata); + bool NetLoop(std::function callback); protected: LRESULT NetMarqueeMode; int NetMaxPos, NetCurPos; @@ -528,7 +528,7 @@ void FBasicStartupScreen :: NetProgress(int count) // //========================================================================== -bool FBasicStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata) +bool FBasicStartupScreen::NetLoop(std::function callback) { BOOL bRet; MSG msg; @@ -549,7 +549,7 @@ bool FBasicStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata { if (msg.message == WM_TIMER && msg.hwnd == Window && msg.wParam == 1337) { - if (timer_callback (userdata)) + if (callback()) { KillTimer (NetStartPane, 1); return true;