Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • STJr/SRB2
  • Sryder/SRB2
  • wolfy852/SRB2
  • Alpha2244/SRB2
  • Inuyasha/SRB2
  • yoshibot/SRB2
  • TehRealSalt/SRB2
  • PrisimaTF/SRB2
  • Hatninja/SRB2
  • SteelT/SRB2
  • james/SRB2
  • ShaderWraith/SRB2
  • SinnamonLat/SRB2
  • mazmazz_/SRB2
  • filpAM/SRB2
  • chaoloveicemdboy/SRB2
  • Whooa21/SRB2
  • Machturne/SRB2
  • Golden/SRB2
  • Tatsuru/SRB2
  • Snu/SRB2
  • Zwip-Zwap_Zapony/SRB2
  • fickleheart/SRB2
  • alphaRexJames/SRB2
  • JJK/SRB2
  • diskpoppy/SRB2
  • Hannu_Hanhi/SRB2
  • ZipperQR/SRB2
  • kays/SRB2
  • spherallic/SRB2
  • Zippy_Zolton/SRB2
  • namiishere/SRB2
  • Ors/SRB2
  • SMS_Alfredo/SRB2
  • sonic_edge/SRB2
  • lavla/SRB2
  • ashi/SRB2
  • X.organic/SRB2
  • Fafabis/SRB2
  • Meziu/SRB2
  • v-rob/SRB2
  • tertu/SRB2
  • bitten2up/SRB2
  • flarn2006/SRB2
  • Krabs/SRB2
  • clairebun/SRB2
  • Lactozilla/SRB2
  • thehackstack/SRB2
  • Spice/SRB2
  • win8linux/SRB2
  • JohnFrostFox/SRB2
  • talktoneon726/SRB2
  • Wane/SRB2
  • Lamibe/SRB2
  • spectrumuk2/srb-2
  • nerdyminer18/srb-2
  • 256nil/SRB2
  • ARJr/SRB2
  • Alam/SRB2
  • Zenya/srb-2-marathon-demos
  • Acelite/srb-2-archivedmodifications
  • MIDIMan/SRB2
  • Lach/SRB2
  • Frostiikin/bounce-tweaks
  • Jaden/SRB2
  • Tyron/SRB2
  • Astronight/SRB2
  • Mari0shi06/SRB2
  • aiire/SRB2
  • Galactice/SRB2
  • srb2-ports/srb2-dreamcast
  • sdasdas/SRB2
  • chreas/srb-2-vr
  • StarManiaKG/the-story-of-sinically-rocketing-and-botching-the-2nd
  • LoganAir/SRB2
  • NepDisk/srb-2
  • alufolie91/SRB2
  • Felicia.iso/SRB2
  • twi/SRB2
  • BarrelsOFun/SRB2
  • Speed2411/SRB2
  • Leather_Realms/SRB2
  • Ayemar/SRB2
  • Acelite/SRB2
  • VladDoc/SRB2
  • kaldrum/model-features
  • strawberryfox417/SRB2
  • Lugent/SRB2
  • Jisk/SRB2
  • Rem/SRB2
  • Refrag/SRB2
  • Henry_3230/srb-3230
  • TehPuertoRicanSpartan2/tprs-srb2
  • Leminn/srb-2-marathon-stuff
  • chromaticpipe2/SRB2
  • MiguelGustavo15/SRB2
  • Maru/srb-2-tests
  • SilicDev/SRB2
  • UnmatchedBracket/SRB2
  • HybridDog/SRB2
  • xordspar0/SRB2
  • jsjhbewfhh/SRB2
  • Fancy2209/SRB2
  • Lorsoen/SRB2
  • shindoukin/SRB2
  • GamerOfDays/SRB2
  • Craftyawesome/SRB2
  • tenshi-tensai-tennoji/SRB2
  • Scarfdudebalder/SRB2
  • luigi-budd/srb-2-fix-interplag-lockon
  • mskluesner/SRB2
  • johnpetersa19/SRB2
  • Pheazant/SRB2
  • chromaticpipe2/srb2classic
  • romoney5/SRB2
  • PAS/SRB2Classic
  • BlueStaggo/SRB2
117 results
Select Git revision
Show changes
Commits on Source (123)
# Find ENet
# Once done, this will define
#
# ENET_FOUND - system has SDL2
# ENET_INCLUDE_DIRS - SDL2 include directories
# ENET_LIBRARIES - link libraries
include(LibFindMacros)
libfind_pkg_check_modules(ENET_PKGCONF ENET)
# includes
find_path(ENET_INCLUDE_DIR
NAMES enet.h
PATHS
${ENET_PKGCONF_INCLUDE_DIRS}
"/usr/include/enet"
"/usr/local/include/enet"
)
# library
find_library(ENET_LIBRARY
NAMES libenet
PATHS
${ENET_PKGCONF_LIBRARY_DIRS}
"/usr/lib"
"/usr/local/lib"
)
# set include dir variables
set(ENET_PROCESS_INCLUDES ENET_INCLUDE_DIR)
set(ENET_PROCESS_LIBS ENET_LIBRARY)
libfind_process(ENET)
......@@ -8,8 +8,9 @@ set(SRB2_CORE_SOURCES
comptime.c
console.c
d_clisrv.c
d_datawrap.c
d_enet.c
d_main.c
d_net.c
d_netcmd.c
d_netfil.c
dehacked.c
......@@ -19,7 +20,6 @@ set(SRB2_CORE_SOURCES
g_game.c
g_input.c
hu_stuff.c
i_tcp.c
info.c
lzf.c
m_anigif.c
......@@ -33,7 +33,6 @@ set(SRB2_CORE_SOURCES
m_queue.c
m_random.c
md5.c
mserv.c
s_sound.c
screen.c
sounds.c
......@@ -53,9 +52,10 @@ set(SRB2_CORE_HEADERS
command.h
console.h
d_clisrv.h
d_datawrap.h
d_enet.h
d_event.h
d_main.h
d_net.h
d_netcmd.h
d_netfil.h
d_player.h
......@@ -75,10 +75,8 @@ set(SRB2_CORE_HEADERS
g_state.h
hu_stuff.h
i_joy.h
i_net.h
i_sound.h
i_system.h
i_tcp.h
i_video.h
info.h
keys.h
......@@ -96,7 +94,6 @@ set(SRB2_CORE_HEADERS
m_random.h
m_swap.h
md5.h
mserv.h
p5prof.h
s_sound.h
screen.h
......@@ -402,6 +399,8 @@ else()
add_definitions(-DNOASM -DNONX86)
endif()
find_package(ENet)
# Targets
# Compatibility flag with later versions of GCC
......
......@@ -414,7 +414,6 @@ OBJS:=$(i_main_o) \
$(OBJDIR)/string.o \
$(OBJDIR)/d_main.o \
$(OBJDIR)/d_clisrv.o \
$(OBJDIR)/d_net.o \
$(OBJDIR)/d_netfil.o \
$(OBJDIR)/d_netcmd.o \
$(OBJDIR)/dehacked.o \
......@@ -473,13 +472,12 @@ OBJS:=$(i_main_o) \
$(OBJDIR)/sounds.o \
$(OBJDIR)/w_wad.o \
$(OBJDIR)/filesrch.o \
$(OBJDIR)/mserv.o \
$(OBJDIR)/i_tcp.o \
$(OBJDIR)/lzf.o \
$(OBJDIR)/lzf.o \
$(OBJDIR)/vid_copy.o \
$(OBJDIR)/d_enet.o \
$(OBJDIR)/d_datawrap.o \
$(OBJDIR)/b_bot.o \
$(i_cdmus_o) \
$(i_net_o) \
$(i_system_o) \
$(i_sound_o) \
$(OBJS)
......
......@@ -273,11 +273,11 @@ void B_RespawnBot(INT32 playernum)
P_TeleportMove(tails, x, y, z);
if (player->charability == CA_FLY)
{
P_SetPlayerMobjState(tails, S_PLAY_ABL1);
P_SetPlayerMobjState(tails, S_PLAY_FLY);
tails->player->powers[pw_tailsfly] = (UINT16)-1;
}
else
P_SetPlayerMobjState(tails, S_PLAY_FALL1);
P_SetPlayerMobjState(tails, S_PLAY_FALL);
P_SetScale(tails, sonic->scale);
tails->destscale = sonic->destscale;
}
......@@ -1225,7 +1225,7 @@ static void Got_NetVar(UINT8 **p, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
......
......@@ -248,11 +248,11 @@ void CON_ReSetupBackColormap(UINT16 num)
{
j = pal[i] + pal[i+1] + pal[i+2];
cwhitemap[k] = (UINT8)(15 - (j>>6));
corangemap[k] = (UINT8)(95 - (j>>6));
cbluemap[k] = (UINT8)(239 - (j>>6));
cgreenmap[k] = (UINT8)(175 - (j>>6));
corangemap[k] = (UINT8)(63 - (j>>6));
cbluemap[k] = (UINT8)(159 - (j>>6));
cgreenmap[k] = (UINT8)(111 - (j>>6));
cgraymap[k] = (UINT8)(31 - (j>>6));
credmap[k] = (UINT8)(143 - (j>>6));
credmap[k] = (UINT8)(47 - (j>>6));
}
}
......@@ -283,11 +283,11 @@ static void CON_SetupBackColormap(void)
{
j = pal[i] + pal[i+1] + pal[i+2];
cwhitemap[k] = (UINT8)(15 - (j>>6));
corangemap[k] = (UINT8)(95 - (j>>6));
cbluemap[k] = (UINT8)(239 - (j>>6));
cgreenmap[k] = (UINT8)(175 - (j>>6));
corangemap[k] = (UINT8)(63 - (j>>6));
cbluemap[k] = (UINT8)(159 - (j>>6));
cgreenmap[k] = (UINT8)(111 - (j>>6));
cgraymap[k] = (UINT8)(31 - (j>>6));
credmap[k] = (UINT8)(143 - (j>>6));
credmap[k] = (UINT8)(47 - (j>>6));
}
// setup the other colormaps, for console text
......@@ -306,20 +306,20 @@ static void CON_SetupBackColormap(void)
orangemap[i] = (UINT8)i;
}
yellowmap[3] = (UINT8)103;
yellowmap[9] = (UINT8)115;
purplemap[3] = (UINT8)195;
purplemap[9] = (UINT8)198;
lgreenmap[3] = (UINT8)162;
lgreenmap[9] = (UINT8)170;
bluemap[3] = (UINT8)228;
bluemap[9] = (UINT8)238;
yellowmap[3] = (UINT8)73;
yellowmap[9] = (UINT8)66;
purplemap[3] = (UINT8)184;
purplemap[9] = (UINT8)186;
lgreenmap[3] = (UINT8)98;
lgreenmap[9] = (UINT8)106;
bluemap[3] = (UINT8)147;
bluemap[9] = (UINT8)158;
graymap[3] = (UINT8)10;
graymap[9] = (UINT8)15;
redmap[3] = (UINT8)124;
redmap[9] = (UINT8)127;
orangemap[3] = (UINT8)85;
orangemap[9] = (UINT8)90;
redmap[3] = (UINT8)210;
redmap[9] = (UINT8)32;
orangemap[3] = (UINT8)52;
orangemap[9] = (UINT8)57;
}
// Setup the console text buffer
......
......@@ -17,10 +17,8 @@
#include <unistd.h> //for unlink
#endif
#include "i_net.h"
#include "i_system.h"
#include "i_video.h"
#include "d_net.h"
#include "d_main.h"
#include "g_game.h"
#include "hu_stuff.h"
......@@ -35,7 +33,6 @@
#include "m_misc.h"
#include "am_map.h"
#include "m_random.h"
#include "mserv.h"
#include "y_inter.h"
#include "r_local.h"
#include "m_argv.h"
......@@ -43,6 +40,7 @@
#include "lzf.h"
#include "lua_script.h"
#include "lua_hook.h"
#include "d_enet.h"
#ifdef CLIENT_LOADINGSCREEN
// cl loading screen
......@@ -58,14 +56,6 @@
// NETWORKING
//
// gametic is the tic about to (or currently being) run
// maketic is the tic that hasn't had control made for it yet
// server:
// nettics is the tic for each node
// firstticstosend is the lowest value of nettics
// client:
// neededtic is the tic needed by the client for run the game
// firstticstosend is used to optimize a condition
// normally maketic >= gametic > 0
#define PREDICTIONQUEUE BACKUPTICS
#define PREDICTIONMASK (PREDICTIONQUEUE-1)
......@@ -77,36 +67,9 @@ static boolean serverrunning = false;
INT32 serverplayer = 0;
char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support)
// server specific vars
UINT8 playernode[MAXPLAYERS];
#ifdef NEWPING
UINT16 pingmeasurecount = 1;
UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone.
UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values.
#endif
SINT8 nodetoplayer[MAXNETNODES];
SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen
boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
static tic_t nettics[MAXNETNODES]; // what tic the client have received
static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet
static UINT8 nodewaiting[MAXNETNODES];
static tic_t firstticstosend; // min of the nettics
static tic_t tictoclear = 0; // optimize d_clearticcmd
static tic_t maketic;
static INT16 consistancy[BACKUPTICS];
// Resynching shit!
static UINT32 resynch_score[MAXNETNODES]; // "score" for kicking -- if this gets too high then cfail kick
static UINT16 resynch_delay[MAXNETNODES]; // delay time before the player can be considered to have desynched
static UINT32 resynch_status[MAXNETNODES]; // 0 bit means synched for that player, 1 means possibly desynched
static UINT8 resynch_sent[MAXNETNODES][MAXNETNODES]; // what synch packets have we attempted to send to the player
static UINT8 resynch_inprogress[MAXNETNODES];
static UINT8 resynch_local_inprogress = false; // WE are desynched and getting packets to fix it.
static UINT8 player_joining = false;
UINT8 hu_resynching = 0;
// client specific
static ticcmd_t localcmds;
......@@ -115,9 +78,6 @@ static boolean cl_packetmissed;
// here it is for the secondary local player (splitscreen)
static UINT8 mynode; // my address pointofview server
static UINT8 localtextcmd[MAXTEXTCMD];
static UINT8 localtextcmd2[MAXTEXTCMD]; // splitscreen
static tic_t neededtic;
SINT8 servernode = 0; // the number of the server node
/// \brief do we accept new players?
/// \todo WORK!
......@@ -142,8 +102,7 @@ typedef struct textcmdtic_s
struct textcmdtic_s *next;
} textcmdtic_t;
ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS];
static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL};
ticcmd_t netcmds[MAXPLAYERS];
static consvar_t cv_showjoinaddress = {"showjoinaddress", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
......@@ -194,14 +153,14 @@ tic_t ExpandTics(INT32 low)
{
INT32 delta;
delta = low - (maketic & UINT8_MAX);
delta = low - (gametic & UINT8_MAX);
if (delta >= -64 && delta <= 64)
return (maketic & ~UINT8_MAX) + low;
return (gametic & ~UINT8_MAX) + low;
else if (delta > 64)
return (maketic & ~UINT8_MAX) - 256 + low;
return (gametic & ~UINT8_MAX) - 256 + low;
else //if (delta < -64)
return (maketic & ~UINT8_MAX) + 256 + low;
return (gametic & ~UINT8_MAX) + 256 + low;
}
// -----------------------------------------------------------------
......@@ -223,197 +182,42 @@ void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum))
void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam)
{
if (localtextcmd[0]+2+nparam > MAXTEXTCMD)
{
// for future reference: if (cv_debug) != debug disabled.
CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[0], sizeu1(nparam));
return;
}
localtextcmd[0]++;
localtextcmd[localtextcmd[0]] = (UINT8)id;
if (param && nparam)
{
M_Memcpy(&localtextcmd[localtextcmd[0]+1], param, nparam);
localtextcmd[0] = (UINT8)(localtextcmd[0] + (UINT8)nparam);
}
//D_NetSendXCmd(id, param, nparam, consoleplayer);
UINT8 **p = (UINT8 **)&param;
(listnetxcmd[id])(p, consoleplayer);
(void)nparam;
}
// splitscreen player
void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam)
{
if (localtextcmd2[0]+2+nparam > MAXTEXTCMD)
{
I_Error("No more place in the buffer for netcmd %d\n",id);
return;
}
localtextcmd2[0]++;
localtextcmd2[localtextcmd2[0]] = (UINT8)id;
if (param && nparam)
{
M_Memcpy(&localtextcmd2[localtextcmd2[0]+1], param, nparam);
localtextcmd2[0] = (UINT8)(localtextcmd2[0] + (UINT8)nparam);
}
//D_NetSendXCmd(id, param, nparam, secondarydisplayplayer);
UINT8 **p = (UINT8 **)&param;
(listnetxcmd[id])(p, secondarydisplayplayer);
(void)nparam;
}
UINT8 GetFreeXCmdSize(void)
{
// -1 for the size and another -1 for the ID.
return (UINT8)(localtextcmd[0] - 2);
return -1;
}
// Frees all textcmd memory for the specified tic
static void D_FreeTextcmd(tic_t tic)
{
textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
textcmdtic_t *textcmdtic = *tctprev;
while (textcmdtic && textcmdtic->tic != tic)
{
tctprev = &textcmdtic->next;
textcmdtic = textcmdtic->next;
}
if (textcmdtic)
{
INT32 i;
// Remove this tic from the list.
*tctprev = textcmdtic->next;
// Free all players.
for (i = 0; i < TEXTCMD_HASH_SIZE; i++)
{
textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[i];
while (textcmdplayer)
{
textcmdplayer_t *tcpnext = textcmdplayer->next;
Z_Free(textcmdplayer);
textcmdplayer = tcpnext;
}
}
// Free this tic's own memory.
Z_Free(textcmdtic);
}
}
// Gets the buffer for the specified ticcmd, or NULL if there isn't one
static UINT8* D_GetExistingTextcmd(tic_t tic, INT32 playernum)
{
textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
while (textcmdtic && textcmdtic->tic != tic) textcmdtic = textcmdtic->next;
// Do we have an entry for the tic? If so, look for player.
if (textcmdtic)
{
textcmdplayer_t *textcmdplayer = textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)];
while (textcmdplayer && textcmdplayer->playernum != playernum) textcmdplayer = textcmdplayer->next;
if (textcmdplayer) return textcmdplayer->cmd;
}
return NULL;
}
// Gets the buffer for the specified ticcmd, creating one if necessary
static UINT8* D_GetTextcmd(tic_t tic, INT32 playernum)
{
textcmdtic_t *textcmdtic = textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
textcmdtic_t **tctprev = &textcmds[tic & (TEXTCMD_HASH_SIZE - 1)];
textcmdplayer_t *textcmdplayer, **tcpprev;
// Look for the tic.
while (textcmdtic && textcmdtic->tic != tic)
{
tctprev = &textcmdtic->next;
textcmdtic = textcmdtic->next;
}
// If we don't have an entry for the tic, make it.
if (!textcmdtic)
{
textcmdtic = *tctprev = Z_Calloc(sizeof (textcmdtic_t), PU_STATIC, NULL);
textcmdtic->tic = tic;
}
tcpprev = &textcmdtic->playercmds[playernum & (TEXTCMD_HASH_SIZE - 1)];
textcmdplayer = *tcpprev;
// Look for the player.
while (textcmdplayer && textcmdplayer->playernum != playernum)
{
tcpprev = &textcmdplayer->next;
textcmdplayer = textcmdplayer->next;
}
// If we don't have an entry for the player, make it.
if (!textcmdplayer)
{
textcmdplayer = *tcpprev = Z_Calloc(sizeof (textcmdplayer_t), PU_STATIC, NULL);
textcmdplayer->playernum = playernum;
}
return textcmdplayer->cmd;
}
static void ExtraDataTicker(void)
static void D_FreeTextcmd(void)
{
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] || i == 0)
{
UINT8 *bufferstart = D_GetExistingTextcmd(gametic, i);
if (bufferstart)
{
UINT8 *curpos = bufferstart;
UINT8 *bufferend = &curpos[curpos[0]+1];
curpos++;
while (curpos < bufferend)
{
if (*curpos < MAXNETXCMD && listnetxcmd[*curpos])
{
const UINT8 id = *curpos;
curpos++;
DEBFILE(va("executing x_cmd %u ply %u ", id, i));
(listnetxcmd[id])(&curpos, i);
DEBFILE("done\n");
}
else
{
if (server)
{
XBOXSTATIC UINT8 buf[3];
buf[0] = (UINT8)i;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
DEBFILE(va("player %d kicked [gametic=%u] reason as follows:\n", i, gametic));
}
CONS_Alert(CONS_WARNING, M_GetText("Got unknown net command [%s]=%d (max %d)\n"), sizeu1(curpos - bufferstart), *curpos, bufferstart[0]);
D_FreeTextcmd(gametic);
return;
}
}
}
}
D_FreeTextcmd(gametic);
// NET TODO
}
static void D_Clearticcmd(tic_t tic)
static void D_Clearticcmd(void)
{
INT32 i;
D_FreeTextcmd(tic);
D_FreeTextcmd();
for (i = 0; i < MAXPLAYERS; i++)
netcmds[tic%BACKUPTICS][i].angleturn = 0;
netcmds[i].angleturn = 0;
DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS));
DEBFILE(va("clear tic\n"));
}
// -----------------------------------------------------------------
......@@ -472,617 +276,18 @@ void ReadLmpExtraData(UINT8 **demo_pointer, INT32 playernum)
// end extra data function for lmps
// -----------------------------------------------------------------
// -----------------------------------------------------------------
// resynch player data
// -----------------------------------------------------------------
static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
{
size_t j;
rsp->playernum = (UINT8)i;
// Do not send anything visual related.
// Only send data that we need to know for physics.
rsp->playerstate = (UINT8)players[i].playerstate; //playerstate_t
rsp->pflags = (UINT32)LONG(players[i].pflags); //pflags_t
rsp->panim = (UINT8)players[i].panim; //panim_t
rsp->aiming = (angle_t)LONG(players[i].aiming);
rsp->currentweapon = LONG(players[i].currentweapon);
rsp->ringweapons = LONG(players[i].ringweapons);
for (j = 0; j < NUMPOWERS; ++j)
rsp->powers[j] = (UINT16)SHORT(players[i].powers[j]);
// Score is resynched in the rspfirm resync packet
rsp->health = 0; // resynched with mo health
rsp->lives = players[i].lives;
rsp->continues = players[i].continues;
rsp->scoreadd = players[i].scoreadd;
rsp->xtralife = players[i].xtralife;
rsp->pity = players[i].pity;
rsp->skincolor = players[i].skincolor;
rsp->skin = LONG(players[i].skin);
// Just in case Lua does something like
// modify these at runtime
rsp->normalspeed = (fixed_t)LONG(players[i].normalspeed);
rsp->runspeed = (fixed_t)LONG(players[i].runspeed);
rsp->thrustfactor = players[i].thrustfactor;
rsp->accelstart = players[i].accelstart;
rsp->acceleration = players[i].acceleration;
rsp->charability = players[i].charability;
rsp->charability2 = players[i].charability2;
rsp->charflags = (UINT32)LONG(players[i].charflags);
rsp->thokitem = (UINT32)LONG(players[i].thokitem); //mobjtype_t
rsp->spinitem = (UINT32)LONG(players[i].spinitem); //mobjtype_t
rsp->revitem = (UINT32)LONG(players[i].revitem); //mobjtype_t
rsp->actionspd = (fixed_t)LONG(players[i].actionspd);
rsp->mindash = (fixed_t)LONG(players[i].mindash);
rsp->maxdash = (fixed_t)LONG(players[i].maxdash);
rsp->jumpfactor = (fixed_t)LONG(players[i].jumpfactor);
rsp->speed = (fixed_t)LONG(players[i].speed);
rsp->jumping = players[i].jumping;
rsp->secondjump = players[i].secondjump;
rsp->fly1 = players[i].fly1;
rsp->glidetime = (tic_t)LONG(players[i].glidetime);
rsp->climbing = players[i].climbing;
rsp->deadtimer = players[i].deadtimer;
rsp->exiting = (tic_t)LONG(players[i].exiting);
rsp->homing = players[i].homing;
rsp->skidtime = (tic_t)LONG(players[i].skidtime);
rsp->cmomx = (fixed_t)LONG(players[i].cmomx);
rsp->cmomy = (fixed_t)LONG(players[i].cmomy);
rsp->rmomx = (fixed_t)LONG(players[i].rmomx);
rsp->rmomy = (fixed_t)LONG(players[i].rmomy);
rsp->weapondelay = LONG(players[i].weapondelay);
rsp->tossdelay = LONG(players[i].tossdelay);
rsp->starpostx = SHORT(players[i].starpostx);
rsp->starposty = SHORT(players[i].starposty);
rsp->starpostz = SHORT(players[i].starpostz);
rsp->starpostnum = LONG(players[i].starpostnum);
rsp->starposttime = (tic_t)LONG(players[i].starposttime);
rsp->starpostangle = (angle_t)LONG(players[i].starpostangle);
rsp->maxlink = LONG(players[i].maxlink);
rsp->dashspeed = (fixed_t)LONG(players[i].dashspeed);
rsp->dashtime = LONG(players[i].dashtime);
rsp->angle_pos = (angle_t)LONG(players[i].angle_pos);
rsp->old_angle_pos = (angle_t)LONG(players[i].old_angle_pos);
rsp->bumpertime = (tic_t)LONG(players[i].bumpertime);
rsp->flyangle = LONG(players[i].flyangle);
rsp->drilltimer = (tic_t)LONG(players[i].drilltimer);
rsp->linkcount = LONG(players[i].linkcount);
rsp->linktimer = (tic_t)LONG(players[i].linktimer);
rsp->anotherflyangle = LONG(players[i].anotherflyangle);
rsp->nightstime = (tic_t)LONG(players[i].nightstime);
rsp->drillmeter = LONG(players[i].drillmeter);
rsp->drilldelay = players[i].drilldelay;
rsp->bonustime = players[i].bonustime;
rsp->mare = players[i].mare;
rsp->lastsidehit = SHORT(players[i].lastsidehit);
rsp->lastlinehit = SHORT(players[i].lastlinehit);
rsp->losstime = (tic_t)LONG(players[i].losstime);
rsp->timeshit = players[i].timeshit;
rsp->onconveyor = LONG(players[i].onconveyor);
rsp->hasmo = false;
//Transfer important mo information if the player has a body.
//This lets us resync players even if they are dead.
if (!players[i].mo)
return;
rsp->hasmo = true;
rsp->health = LONG(players[i].mo->health);
rsp->angle = (angle_t)LONG(players[i].mo->angle);
rsp->x = LONG(players[i].mo->x);
rsp->y = LONG(players[i].mo->y);
rsp->z = LONG(players[i].mo->z);
rsp->momx = LONG(players[i].mo->momx);
rsp->momy = LONG(players[i].mo->momy);
rsp->momz = LONG(players[i].mo->momz);
rsp->friction = LONG(players[i].mo->friction);
rsp->movefactor = LONG(players[i].mo->movefactor);
rsp->tics = LONG(players[i].mo->tics);
rsp->statenum = (statenum_t)LONG(players[i].mo->state-states); // :(
rsp->eflags = (UINT16)SHORT(players[i].mo->eflags);
rsp->flags = LONG(players[i].mo->flags);
rsp->flags2 = LONG(players[i].mo->flags2);
rsp->radius = LONG(players[i].mo->radius);
rsp->height = LONG(players[i].mo->height);
rsp->scale = LONG(players[i].mo->scale);
rsp->destscale = LONG(players[i].mo->destscale);
rsp->scalespeed = LONG(players[i].mo->scalespeed);
}
static void resynch_read_player(resynch_pak *rsp)
{
INT32 i = rsp->playernum, j;
mobj_t *savedmo = players[i].mo;
// Do not send anything visual related.
// Only send data that we need to know for physics.
players[i].playerstate = (UINT8)rsp->playerstate; //playerstate_t
players[i].pflags = (UINT32)LONG(rsp->pflags); //pflags_t
players[i].panim = (UINT8)rsp->panim; //panim_t
players[i].aiming = (angle_t)LONG(rsp->aiming);
players[i].currentweapon = LONG(rsp->currentweapon);
players[i].ringweapons = LONG(rsp->ringweapons);
for (j = 0; j < NUMPOWERS; ++j)
players[i].powers[j] = (UINT16)SHORT(rsp->powers[j]);
// Score is resynched in the rspfirm resync packet
players[i].health = rsp->health;
players[i].lives = rsp->lives;
players[i].continues = rsp->continues;
players[i].scoreadd = rsp->scoreadd;
players[i].xtralife = rsp->xtralife;
players[i].pity = rsp->pity;
players[i].skincolor = rsp->skincolor;
players[i].skin = LONG(rsp->skin);
// Just in case Lua does something like
// modify these at runtime
players[i].normalspeed = (fixed_t)LONG(rsp->normalspeed);
players[i].runspeed = (fixed_t)LONG(rsp->runspeed);
players[i].thrustfactor = rsp->thrustfactor;
players[i].accelstart = rsp->accelstart;
players[i].acceleration = rsp->acceleration;
players[i].charability = rsp->charability;
players[i].charability2 = rsp->charability2;
players[i].charflags = (UINT32)LONG(rsp->charflags);
players[i].thokitem = (UINT32)LONG(rsp->thokitem); //mobjtype_t
players[i].spinitem = (UINT32)LONG(rsp->spinitem); //mobjtype_t
players[i].revitem = (UINT32)LONG(rsp->revitem); //mobjtype_t
players[i].actionspd = (fixed_t)LONG(rsp->actionspd);
players[i].mindash = (fixed_t)LONG(rsp->mindash);
players[i].maxdash = (fixed_t)LONG(rsp->maxdash);
players[i].jumpfactor = (fixed_t)LONG(rsp->jumpfactor);
players[i].speed = (fixed_t)LONG(rsp->speed);
players[i].jumping = rsp->jumping;
players[i].secondjump = rsp->secondjump;
players[i].fly1 = rsp->fly1;
players[i].glidetime = (tic_t)LONG(rsp->glidetime);
players[i].climbing = rsp->climbing;
players[i].deadtimer = rsp->deadtimer;
players[i].exiting = (tic_t)LONG(rsp->exiting);
players[i].homing = rsp->homing;
players[i].skidtime = (tic_t)LONG(rsp->skidtime);
players[i].cmomx = (fixed_t)LONG(rsp->cmomx);
players[i].cmomy = (fixed_t)LONG(rsp->cmomy);
players[i].rmomx = (fixed_t)LONG(rsp->rmomx);
players[i].rmomy = (fixed_t)LONG(rsp->rmomy);
players[i].weapondelay = LONG(rsp->weapondelay);
players[i].tossdelay = LONG(rsp->tossdelay);
players[i].starpostx = SHORT(rsp->starpostx);
players[i].starposty = SHORT(rsp->starposty);
players[i].starpostz = SHORT(rsp->starpostz);
players[i].starpostnum = LONG(rsp->starpostnum);
players[i].starposttime = (tic_t)LONG(rsp->starposttime);
players[i].starpostangle = (angle_t)LONG(rsp->starpostangle);
players[i].maxlink = LONG(rsp->maxlink);
players[i].dashspeed = (fixed_t)LONG(rsp->dashspeed);
players[i].dashtime = LONG(rsp->dashtime);
players[i].angle_pos = (angle_t)LONG(rsp->angle_pos);
players[i].old_angle_pos = (angle_t)LONG(rsp->old_angle_pos);
players[i].bumpertime = (tic_t)LONG(rsp->bumpertime);
players[i].flyangle = LONG(rsp->flyangle);
players[i].drilltimer = (tic_t)LONG(rsp->drilltimer);
players[i].linkcount = LONG(rsp->linkcount);
players[i].linktimer = (tic_t)LONG(rsp->linktimer);
players[i].anotherflyangle = LONG(rsp->anotherflyangle);
players[i].nightstime = (tic_t)LONG(rsp->nightstime);
players[i].drillmeter = LONG(rsp->drillmeter);
players[i].drilldelay = rsp->drilldelay;
players[i].bonustime = rsp->bonustime;
players[i].mare = rsp->mare;
players[i].lastsidehit = SHORT(rsp->lastsidehit);
players[i].lastlinehit = SHORT(rsp->lastlinehit);
players[i].losstime = (tic_t)LONG(rsp->losstime);
players[i].timeshit = rsp->timeshit;
players[i].onconveyor = LONG(rsp->onconveyor);
//We get a packet for each player in game.
if (!playeringame[i])
return;
//...but keep old mo even if it is corrupt or null!
players[i].mo = savedmo;
//Transfer important mo information if they have a valid mo.
if (!rsp->hasmo)
return;
//server thinks player has a body.
//Give them a new body that can be then manipulated by the server's info.
if (!players[i].mo) //client thinks it has no body.
P_SpawnPlayer(i);
//At this point, the player should have a body, whether they were respawned or not.
P_UnsetThingPosition(players[i].mo);
players[i].mo->angle = (angle_t)LONG(rsp->angle);
players[i].mo->eflags = (UINT16)SHORT(rsp->eflags);
players[i].mo->flags = LONG(rsp->flags);
players[i].mo->flags2 = LONG(rsp->flags2);
players[i].mo->friction = LONG(rsp->friction);
players[i].mo->health = LONG(rsp->health);
players[i].mo->momx = LONG(rsp->momx);
players[i].mo->momy = LONG(rsp->momy);
players[i].mo->momz = LONG(rsp->momz);
players[i].mo->movefactor = LONG(rsp->movefactor);
players[i].mo->tics = LONG(rsp->tics);
P_SetMobjStateNF(players[i].mo, LONG(rsp->statenum));
players[i].mo->x = LONG(rsp->x);
players[i].mo->y = LONG(rsp->y);
players[i].mo->z = LONG(rsp->z);
players[i].mo->radius = LONG(rsp->radius);
players[i].mo->height = LONG(rsp->height);
// P_SetScale is redundant for this, as all related variables are already restored properly.
players[i].mo->scale = LONG(rsp->scale);
players[i].mo->destscale = LONG(rsp->destscale);
players[i].mo->scalespeed = LONG(rsp->scalespeed);
// And finally, SET THE MOBJ SKIN damn it.
players[i].mo->skin = &skins[players[i].skin];
players[i].mo->color = players[i].skincolor;
P_SetThingPosition(players[i].mo);
}
static inline void resynch_write_ctf(resynchend_pak *rst)
{
mobj_t *mflag;
UINT8 i, j;
for (i = 0, mflag = redflag; i < 2; ++i, mflag = blueflag)
{
rst->flagx[i] = rst->flagy[i] = rst->flagz[i] = 0;
rst->flagloose[i] = rst->flagflags[i] = 0;
rst->flagplayer[i] = -1;
if (!mflag)
{
// Should be held by a player
for (j = 0; j < MAXPLAYERS; ++j)
{
// GF_REDFLAG is 1, GF_BLUEFLAG is 2
// redflag handling is i=0, blueflag is i=1
// so check for gotflag == (i+1)
if (!playeringame[j] || players[j].gotflag != (i+1))
continue;
rst->flagplayer[i] = (SINT8)j;
break;
}
if (j == MAXPLAYERS) // fine, no I_Error
{
CONS_Alert(CONS_ERROR, "One of the flags has gone completely missing...\n");
rst->flagplayer[i] = -2;
}
continue;
}
rst->flagx[i] = (fixed_t)LONG(mflag->x);
rst->flagy[i] = (fixed_t)LONG(mflag->y);
rst->flagz[i] = (fixed_t)LONG(mflag->z);
rst->flagflags[i] = LONG(mflag->flags2);
rst->flagloose[i] = LONG(mflag->fuse); // Dropped or not?
}
}
static inline void resynch_read_ctf(resynchend_pak *p)
{
UINT8 i;
for (i = 0; i < MAXPLAYERS; ++i)
players[i].gotflag = 0;
// Red flag
if (p->flagplayer[0] == -2)
; // The server doesn't even know what happened to it...
else if (p->flagplayer[0] != -1) // Held by a player
{
if (!playeringame[p->flagplayer[0]])
I_Error("Invalid red flag player %d who isn't in the game!", (INT32)p->flagplayer[0]);
players[p->flagplayer[0]].gotflag = GF_REDFLAG;
if (redflag)
{
P_RemoveMobj(redflag);
redflag = NULL;
}
}
else
{
if (!redflag)
redflag = P_SpawnMobj(0,0,0,MT_REDFLAG);
P_UnsetThingPosition(redflag);
redflag->x = (fixed_t)LONG(p->flagx[0]);
redflag->y = (fixed_t)LONG(p->flagy[0]);
redflag->z = (fixed_t)LONG(p->flagz[0]);
redflag->flags2 = LONG(p->flagflags[0]);
redflag->fuse = LONG(p->flagloose[0]);
P_SetThingPosition(redflag);
}
// Blue flag
if (p->flagplayer[1] == -2)
; // The server doesn't even know what happened to it...
else if (p->flagplayer[1] != -1) // Held by a player
{
if (!playeringame[p->flagplayer[1]])
I_Error("Invalid blue flag player %d who isn't in the game!", (INT32)p->flagplayer[1]);
players[p->flagplayer[1]].gotflag = GF_BLUEFLAG;
if (blueflag)
{
P_RemoveMobj(blueflag);
blueflag = NULL;
}
}
else
{
if (!blueflag)
blueflag = P_SpawnMobj(0,0,0,MT_BLUEFLAG);
P_UnsetThingPosition(blueflag);
blueflag->x = (fixed_t)LONG(p->flagx[1]);
blueflag->y = (fixed_t)LONG(p->flagy[1]);
blueflag->z = (fixed_t)LONG(p->flagz[1]);
blueflag->flags2 = LONG(p->flagflags[1]);
blueflag->fuse = LONG(p->flagloose[1]);
P_SetThingPosition(blueflag);
}
}
static inline void resynch_write_others(resynchend_pak *rst)
{
UINT8 i;
rst->ingame = rst->ctfteam = 0;
for (i = 0; i < MAXPLAYERS; ++i)
{
if (!playeringame[i])
{
rst->score[i] = 0;
rst->numboxes[i] = 0;
rst->totalring[i] = 0;
rst->realtime[i] = 0;
rst->laps[i] = 0;
continue;
}
if (!players[i].spectator)
{
rst->ingame |= (1<<i);
if (players[i].ctfteam > 1)
rst->ctfteam |= (1<<i);
}
rst->score[i] = (UINT32)LONG(players[i].score);
rst->numboxes[i] = SHORT(players[i].numboxes);
rst->totalring[i] = SHORT(players[i].totalring);
rst->realtime[i] = (tic_t)LONG(players[i].realtime);
rst->laps[i] = players[i].laps;
}
// endian safeness
rst->ingame = (UINT32)LONG(rst->ingame);
rst->ctfteam = (UINT32)LONG(rst->ctfteam);
}
static inline void resynch_read_others(resynchend_pak *p)
{
UINT8 i;
UINT32 loc_ingame = (UINT32)LONG(p->ingame);
UINT32 loc_ctfteam = (UINT32)LONG(p->ctfteam);
for (i = 0; i < MAXPLAYERS; ++i)
{
// We don't care if they're in the game or not, just write all the data.
if (loc_ingame & (1<<i))
{
players[i].spectator = false;
players[i].ctfteam = (loc_ctfteam & (1<<i)) ? 2 : 1;
}
else
{
players[i].spectator = true;
players[i].ctfteam = 0;
}
players[i].score = (UINT32)LONG(p->score[i]);
players[i].numboxes = SHORT(p->numboxes[i]);
players[i].totalring = SHORT(p->totalring[i]);
players[i].realtime = (tic_t)LONG(p->realtime[i]);
players[i].laps = p->laps[i];
}
}
static void SV_InitResynchVars(INT32 node)
{
resynch_delay[node] = TICRATE; // initial one second delay
resynch_score[node] = 0; // clean slate
resynch_status[node] = 0x00;
resynch_inprogress[node] = false;
memset(resynch_sent[node], 0, MAXNETNODES);
}
static void SV_RequireResynch(INT32 node)
{
INT32 i;
resynch_delay[node] = 10; // Delay before you can fail sync again
resynch_score[node] += 200; // Add score for initial desynch
resynch_status[node] = 0xFF; // No players assumed synched
resynch_inprogress[node] = true; // so we know to send a PT_RESYNCHEND after sync
// Initial setup
memset(resynch_sent[node], 0, MAXNETNODES);
for (i = 0; i < MAXPLAYERS; ++i)
{
if (!playeringame[i]) // Player not in game so just drop it from required synch
resynch_status[node] &= ~(1<<i);
else if (i == node); // instantly update THEIR position
else // Send at random times based on num players
resynch_sent[node][i] = M_RandomKey(D_NumPlayers()>>1)+1;
}
}
static void SV_SendResynch(INT32 node)
{
INT32 i, j;
if (!nodeingame[node])
{
// player left during resynch
// so obviously we don't need to do any of this anymore
resynch_inprogress[node] = false;
return;
}
// resynched?
if (!resynch_status[node])
{
// you are now synched
resynch_inprogress[node] = false;
netbuffer->packettype = PT_RESYNCHEND;
netbuffer->u.resynchend.randomseed = P_GetRandSeed();
if (gametype == GT_CTF)
resynch_write_ctf(&netbuffer->u.resynchend);
resynch_write_others(&netbuffer->u.resynchend);
HSendPacket(node, true, 0, (sizeof(resynchend_pak)));
return;
}
netbuffer->packettype = PT_RESYNCHING;
for (i = 0, j = 0; i < MAXPLAYERS; ++i)
{
// if already synched don't bother
if (!(resynch_status[node] & 1<<i))
continue;
// waiting for a reply or just waiting in general
if (resynch_sent[node][i])
{
--resynch_sent[node][i];
continue;
}
resynch_write_player(&netbuffer->u.resynchpak, i);
HSendPacket(node, false, 0, (sizeof(resynch_pak)));
resynch_sent[node][i] = TICRATE;
resynch_score[node] += 2; // penalty for send
if (++j > 3)
break;
}
if (resynch_score[node] > (unsigned)cv_resynchattempts.value*250)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)nodetoplayer[node];
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
resynch_score[node] = 0;
}
}
static void CL_AcknowledgeResynch(resynch_pak *rsp)
{
resynch_read_player(rsp);
netbuffer->packettype = PT_RESYNCHGET;
netbuffer->u.resynchgot = rsp->playernum;
HSendPacket(servernode, true, 0, sizeof(UINT8));
}
static void SV_AcknowledgeResynchAck(INT32 node, UINT8 rsg)
{
if (rsg >= MAXPLAYERS)
resynch_score[node] += 16384; // lol.
else
{
resynch_status[node] &= ~(1<<rsg);
--resynch_score[node]; // unpenalize
}
}
// -----------------------------------------------------------------
// end resynch
// -----------------------------------------------------------------
static INT16 Consistancy(void);
#ifndef NONET
#define JOININGAME
#endif
typedef enum
{
cl_searching,
cl_downloadfiles,
cl_askjoin,
cl_waitjoinresponse,
#ifdef JOININGAME
cl_downloadsavegame,
#endif
cl_connected,
cl_aborted
} cl_mode_t;
static void GetPackets(void);
static cl_mode_t cl_mode = cl_searching;
// Player name send/load
static void CV_SavePlayerNames(UINT8 **p)
{
INT32 i = 0;
// Players in game only.
for (; i < MAXPLAYERS; ++i)
{
if (!playeringame[i])
{
WRITEUINT8(*p, 0);
continue;
}
WRITESTRING(*p, player_names[i]);
}
}
static void CV_LoadPlayerNames(UINT8 **p)
{
INT32 i = 0;
char tmp_name[MAXPLAYERNAME+1];
tmp_name[MAXPLAYERNAME] = 0;
for (; i < MAXPLAYERS; ++i)
{
READSTRING(*p, tmp_name);
if (tmp_name[0] == 0)
continue;
if (tmp_name[MAXPLAYERNAME]) // overflow detected
I_Error("Received bad server config packet when trying to join");
memcpy(player_names[i], tmp_name, MAXPLAYERNAME+1);
}
}
#ifdef CLIENT_LOADINGSCREEN
//
// CL_DrawConnectionStatus
......@@ -1100,10 +305,9 @@ static inline void CL_DrawConnectionStatus(void)
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort");
if (cl_mode != cl_downloadfiles)
{
INT32 i, animtime = ((ccstime / 4) & 15) + 16;
UINT8 palstart = (cl_mode == cl_searching) ? 128 : 160;
UINT8 palstart = (cl_mode == cl_searching) ? 32 : 96;
// 15 pal entries total.
const char *cltext;
......@@ -1112,16 +316,6 @@ static inline void CL_DrawConnectionStatus(void)
switch (cl_mode)
{
#ifdef JOININGAME
case cl_downloadsavegame:
cltext = M_GetText("Downloading game state...");
Net_GetNetStat();
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
va(" %4uK",fileneeded[lastfilenum].currentsize>>10));
V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
va("%3.1fK/s ", ((double)getbps)/1024));
break;
#endif
case cl_askjoin:
case cl_waitjoinresponse:
cltext = M_GetText("Requesting to join...");
......@@ -1132,28 +326,6 @@ static inline void CL_DrawConnectionStatus(void)
}
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP, cltext);
}
else
{
INT32 dldlength;
static char tempname[32];
Net_GetNetStat();
dldlength = (INT32)((fileneeded[lastfilenum].currentsize/(double)fileneeded[lastfilenum].totalsize) * 256);
if (dldlength > 256)
dldlength = 256;
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175);
V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, dldlength, 8, 160);
memset(tempname, 0, sizeof(tempname));
nameonly(strncpy(tempname, fileneeded[lastfilenum].filename, 31));
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP,
va(M_GetText("Downloading \"%s\""), tempname));
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,fileneeded[lastfilenum].totalsize>>10));
V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
va("%3.1fK/s ", ((double)getbps)/1024));
}
}
#endif
......@@ -1164,687 +336,66 @@ static inline void CL_DrawConnectionStatus(void)
// used only in arbitratrenetstart()
static boolean CL_SendJoin(void)
{
UINT8 localplayers = 1;
if (netgame)
CONS_Printf(M_GetText("Sending join request...\n"));
netbuffer->packettype = PT_CLIENTJOIN;
if (splitscreen || botingame)
localplayers++;
netbuffer->u.clientcfg.localplayers = localplayers;
netbuffer->u.clientcfg.version = VERSION;
netbuffer->u.clientcfg.subversion = SUBVERSION;
CONS_Printf("NETWORK: Sending join request.\n");
if (!netgame)
{
nodewaiting[servernode]++;
if (splitscreen || botingame)
nodewaiting[servernode]++;
return true;
}
if (server) // no need to ask yourself if you can join!
return true;
else
Net_SendJoin();
return true;
}
return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
void CL_ConnectionSuccessful(void)
{
if (cl_mode == cl_waitjoinresponse)
cl_mode = cl_connected;
}
static void SV_SendServerInfo(INT32 node, tic_t servertime)
// use adaptive send using net_bandwidth and stat.sendbytes
static void CL_ConnectToServer(void)
{
UINT8 *p;
netbuffer->packettype = PT_SERVERINFO;
netbuffer->u.serverinfo.version = VERSION;
netbuffer->u.serverinfo.subversion = SUBVERSION;
// return back the time value so client can compute their ping
netbuffer->u.serverinfo.time = (tic_t)LONG(servertime);
netbuffer->u.serverinfo.leveltime = (tic_t)LONG(leveltime);
netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
netbuffer->u.serverinfo.gametype = (UINT8)gametype;
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated;
strncpy(netbuffer->u.serverinfo.servername, cv_servername.string,
MAXSERVERNAME);
strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7);
M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16);
if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, ""))
strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33);
else
strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 33);
INT32 pnumnodes, nodewaited = net_nodecount, i;
boolean waitmore;
tic_t oldtic;
netbuffer->u.serverinfo.maptitle[32] = '\0';
cl_mode = cl_askjoin;
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
netbuffer->u.serverinfo.iszone = 1;
else
netbuffer->u.serverinfo.iszone = 0;
#ifdef CLIENT_LOADINGSCREEN
lastfilenum = 0;
#endif
netbuffer->u.serverinfo.actnum = mapheaderinfo[gamemap-1]->actnum;
if (netgame)
{
if (servernode < 0 || servernode >= MAXNETNODES)
CONS_Printf(M_GetText("Searching for a server...\n"));
else
CONS_Printf(M_GetText("Contacting the server...\n"));
}
p = PutFileNeeded();
if (gamestate == GS_INTERMISSION)
Y_EndIntermission(); // clean up intermission graphics etc
HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u));
}
DEBFILE(va("waiting %d nodes\n", net_nodecount));
G_SetGamestate(GS_WAITINGPLAYERS);
wipegamestate = GS_WAITINGPLAYERS;
static void SV_SendPlayerInfo(INT32 node)
{
UINT8 i;
netbuffer->packettype = PT_PLAYERINFO;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
{
netbuffer->u.playerinfo[i].node = 255; // This slot is empty.
continue;
}
netbuffer->u.playerinfo[i].node = (UINT8)playernode[i];
strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1);
netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0';
//fetch IP address
{
const char *claddress;
UINT32 numericaddress[4];
memset(netbuffer->u.playerinfo[i].address, 0, 4);
if (playernode[i] == 0)
{
//127.0.0.1
netbuffer->u.playerinfo[i].address[0] = 127;
netbuffer->u.playerinfo[i].address[3] = 1;
}
else if (playernode[i] > 0 && I_GetNodeAddress && (claddress = I_GetNodeAddress(playernode[i])) != NULL)
{
if (sscanf(claddress, "%d.%d.%d.%d", &numericaddress[0], &numericaddress[1], &numericaddress[2], &numericaddress[3]) < 4)
goto badaddress;
netbuffer->u.playerinfo[i].address[0] = (UINT8)numericaddress[0];
netbuffer->u.playerinfo[i].address[1] = (UINT8)numericaddress[1];
netbuffer->u.playerinfo[i].address[2] = (UINT8)numericaddress[2];
netbuffer->u.playerinfo[i].address[3] = (UINT8)numericaddress[3];
}
}
badaddress:
if (G_GametypeHasTeams())
{
if (!players[i].ctfteam)
netbuffer->u.playerinfo[i].team = 255;
else
netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam;
}
else
{
if (players[i].spectator)
netbuffer->u.playerinfo[i].team = 255;
else
netbuffer->u.playerinfo[i].team = 0;
}
netbuffer->u.playerinfo[i].score = LONG(players[i].score);
netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE));
netbuffer->u.playerinfo[i].skin = (UINT8)players[i].skin;
// Extra data
netbuffer->u.playerinfo[i].data = players[i].skincolor;
if (players[i].pflags & PF_TAGIT)
netbuffer->u.playerinfo[i].data |= 0x20;
if (players[i].gotflag)
netbuffer->u.playerinfo[i].data |= 0x40;
if (players[i].powers[pw_super])
netbuffer->u.playerinfo[i].data |= 0x80;
}
HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS);
}
static boolean SV_SendServerConfig(INT32 node)
{
INT32 i;
UINT8 *p, *op;
boolean waspacketsent;
netbuffer->packettype = PT_SERVERCFG;
netbuffer->u.servercfg.version = VERSION;
netbuffer->u.servercfg.subversion = SUBVERSION;
netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer;
netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots);
netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic);
netbuffer->u.servercfg.clientnode = (UINT8)node;
netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
netbuffer->u.servercfg.gametype = (UINT8)gametype;
netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
netbuffer->u.servercfg.adminplayer = (SINT8)adminplayer;
// we fill these structs with FFs so that any players not in game get sent as 0xFFFF
// which is nice and easy for us to detect
memset(netbuffer->u.servercfg.playerskins, 0xFF, sizeof(netbuffer->u.servercfg.playerskins));
memset(netbuffer->u.servercfg.playercolor, 0xFF, sizeof(netbuffer->u.servercfg.playercolor));
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
netbuffer->u.servercfg.playerskins[i] = (UINT8)players[i].skin;
netbuffer->u.servercfg.playercolor[i] = (UINT8)players[i].skincolor;
}
memcpy(netbuffer->u.servercfg.server_context, server_context, 8);
op = p = netbuffer->u.servercfg.varlengthinputs;
CV_SavePlayerNames(&p);
CV_SaveNetVars(&p);
{
const size_t len = sizeof (serverconfig_pak) + (size_t)(p - op);
#ifdef DEBUGFILE
if (debugfile)
{
fprintf(debugfile, "ServerConfig Packet about to be sent, size of packet:%s to node:%d\n",
sizeu1(len), node);
}
#endif
waspacketsent = HSendPacket(node, true, 0, len);
}
#ifdef DEBUGFILE
if (debugfile)
{
if (waspacketsent)
{
fprintf(debugfile, "ServerConfig Packet was sent\n");
}
else
{
fprintf(debugfile, "ServerConfig Packet could not be sent right now\n");
}
}
#endif
return waspacketsent;
}
#ifdef JOININGAME
#define SAVEGAMESIZE (768*1024)
static void SV_SendSaveGame(INT32 node)
{
size_t length, compressedlen;
UINT8 *savebuffer;
UINT8 *compressedsave;
UINT8 *buffertosend;
// first save it in a malloced buffer
savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
if (!savebuffer)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return;
}
// Leave room for the uncompressed length.
save_p = savebuffer + sizeof(UINT32);
P_SaveNetGame();
length = save_p - savebuffer;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
// Allocate space for compressed save: one byte fewer than for the
// uncompressed data to ensure that the compression is worthwhile.
compressedsave = malloc(length - 1);
if (!compressedsave)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return;
}
// Attempt to compress it.
if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1)))
{
// Compressing succeeded; send compressed data
free(savebuffer);
// State that we're compressed.
buffertosend = compressedsave;
WRITEUINT32(compressedsave, length - sizeof(UINT32));
length = compressedlen + sizeof(UINT32);
}
else
{
// Compression failed to make it smaller; send original
free(compressedsave);
// State that we're not compressed
buffertosend = savebuffer;
WRITEUINT32(savebuffer, 0);
}
SendRam(node, buffertosend, length, SF_RAM, 0);
save_p = NULL;
}
#ifdef DUMPCONSISTENCY
#define TMPSAVENAME "badmath.sav"
static consvar_t cv_dumpconsistency = {"dumpconsistency", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
static void SV_SavedGame(void)
{
size_t length;
UINT8 *savebuffer;
XBOXSTATIC char tmpsave[256];
if (!cv_dumpconsistency.value)
return;
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
// first save it in a malloced buffer
save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE);
if (!save_p)
{
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return;
}
P_SaveNetGame();
length = save_p - savebuffer;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
// then save it!
if (!FIL_WriteFile(tmpsave, savebuffer, length))
CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave);
free(savebuffer);
save_p = NULL;
}
#undef TMPSAVENAME
#endif
#define TMPSAVENAME "$$$.sav"
static void CL_LoadReceivedSavegame(void)
{
UINT8 *savebuffer = NULL;
size_t length, decompressedlen;
XBOXSTATIC char tmpsave[256];
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
length = FIL_ReadFile(tmpsave, &savebuffer);
CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length));
if (!length)
{
I_Error("Can't read savegame sent");
return;
}
save_p = savebuffer;
// Decompress saved game if necessary.
decompressedlen = READUINT32(save_p);
if(decompressedlen > 0)
{
UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL);
lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen);
Z_Free(savebuffer);
save_p = savebuffer = decompressedbuffer;
}
paused = false;
demoplayback = false;
titledemo = false;
automapactive = false;
// load a base level
playerdeadview = false;
if (P_LoadNetGame())
{
const INT32 actnum = mapheaderinfo[gamemap-1]->actnum;
CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap));
if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, ""))
{
CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl);
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
CONS_Printf(M_GetText("ZONE"));
if (actnum > 0)
CONS_Printf(" %2d", actnum);
}
CONS_Printf("\"\n");
}
else
{
CONS_Alert(CONS_ERROR, M_GetText("Can't load the level!\n"));
Z_Free(savebuffer);
save_p = NULL;
if (unlink(tmpsave) == -1)
CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
return;
}
// done
Z_Free(savebuffer);
save_p = NULL;
if (unlink(tmpsave) == -1)
CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
consistancy[gametic%BACKUPTICS] = Consistancy();
CON_ToggleOff();
}
#endif
#ifndef NONET
static void SendAskInfo(INT32 node, boolean viams)
{
const tic_t asktime = I_GetTime();
netbuffer->packettype = PT_ASKINFO;
netbuffer->u.askinfo.version = VERSION;
netbuffer->u.askinfo.time = (tic_t)LONG(asktime);
// Even if this never arrives due to the host being firewalled, we've
// now allowed traffic from the host to us in, so once the MS relays
// our address to the host, it'll be able to speak to us.
HSendPacket(node, false, 0, sizeof (askinfo_pak));
// Also speak to the MS.
if (viams && node != 0 && node != BROADCASTADDR)
SendAskInfoViaMS(node, asktime);
}
serverelem_t serverlist[MAXSERVERLIST];
UINT32 serverlistcount = 0;
static void SL_ClearServerList(INT32 connectedserver)
{
UINT32 i;
for (i = 0; i < serverlistcount; i++)
if (connectedserver != serverlist[i].node)
{
Net_CloseConnection(serverlist[i].node);
serverlist[i].node = 0;
}
serverlistcount = 0;
}
static UINT32 SL_SearchServer(INT32 node)
{
UINT32 i;
for (i = 0; i < serverlistcount; i++)
if (serverlist[i].node == node)
return i;
return UINT32_MAX;
}
static void SL_InsertServer(serverinfo_pak* info, SINT8 node)
{
UINT32 i;
// search if not already on it
i = SL_SearchServer(node);
if (i == UINT32_MAX)
{
// not found add it
if (serverlistcount >= MAXSERVERLIST)
return; // list full
if (info->version != VERSION)
return; // Not same version.
if (info->subversion != SUBVERSION)
return; // Close, but no cigar.
i = serverlistcount++;
}
serverlist[i].info = *info;
serverlist[i].node = node;
// resort server list
M_SortServerList();
}
void CL_UpdateServerList(boolean internetsearch, INT32 room)
{
SL_ClearServerList(0);
if (!netgame && I_NetOpenSocket)
{
MSCloseUDPSocket(); // Tidy up before wiping the slate.
if (I_NetOpenSocket())
{
netgame = true;
multiplayer = true;
}
}
// search for local servers
if (netgame)
SendAskInfo(BROADCASTADDR, false);
if (internetsearch)
{
const msg_server_t *server_list;
INT32 i = -1;
server_list = GetShortServersList(room);
if (server_list)
{
char version[8] = "";
#if VERSION > 0 || SUBVERSION > 0
snprintf(version, sizeof (version), "%d.%d.%d", VERSION/100, VERSION%100, SUBVERSION);
#else
strcpy(version, GetRevisionString());
#endif
version[sizeof (version) - 1] = '\0';
for (i = 0; server_list[i].header.buffer[0]; i++)
{
// Make sure MS version matches our own, to
// thwart nefarious servers who lie to the MS.
if(strcmp(version, server_list[i].version) == 0)
{
INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port);
if (node == -1)
break; // no more node free
SendAskInfo(node, true);
}
}
}
//no server list?(-1) or no servers?(0)
if (!i)
{
; /// TODO: display error or warning?
}
}
}
#endif // ifndef NONET
// use adaptive send using net_bandwidth and stat.sendbytes
static void CL_ConnectToServer(boolean viams)
{
INT32 pnumnodes, nodewaited = doomcom->numnodes, i;
boolean waitmore;
tic_t oldtic;
#ifndef NONET
tic_t asksent;
#endif
#ifdef JOININGAME
XBOXSTATIC char tmpsave[256];
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
#endif
cl_mode = cl_searching;
#ifdef CLIENT_LOADINGSCREEN
lastfilenum = 0;
#endif
#ifdef JOININGAME
// don't get a corrupt savegame error because tmpsave already exists
if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
I_Error("Can't delete %s\n", tmpsave);
#endif
if (netgame)
{
if (servernode < 0 || servernode >= MAXNETNODES)
CONS_Printf(M_GetText("Searching for a server...\n"));
else
CONS_Printf(M_GetText("Contacting the server...\n"));
}
if (gamestate == GS_INTERMISSION)
Y_EndIntermission(); // clean up intermission graphics etc
DEBFILE(va("waiting %d nodes\n", doomcom->numnodes));
G_SetGamestate(GS_WAITINGPLAYERS);
wipegamestate = GS_WAITINGPLAYERS;
adminplayer = -1;
pnumnodes = 1;
oldtic = I_GetTime() - 1;
#ifndef NONET
asksent = (tic_t)-TICRATE;
i = SL_SearchServer(servernode);
if (i != -1)
{
INT32 j;
const char *gametypestr = NULL;
CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername);
for (j = 0; gametype_cons_t[j].strvalue; j++)
{
if (gametype_cons_t[j].value == serverlist[i].info.gametype)
{
gametypestr = gametype_cons_t[j].strvalue;
break;
}
}
if (gametypestr)
CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr);
CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100,
serverlist[i].info.version%100, serverlist[i].info.subversion);
}
SL_ClearServerList(servernode);
#endif
adminplayer = -1;
pnumnodes = 1;
oldtic = I_GetTime() - 1;
CONS_Printf("NETWORK: Entering connect loop.\n");
do
{
switch (cl_mode)
{
case cl_searching:
#ifndef NONET
// serverlist is updated by GetPacket function
if (serverlistcount > 0)
{
// this can be a responce to our broadcast request
if (servernode == -1 || servernode >= MAXNETNODES)
{
i = 0;
servernode = serverlist[i].node;
CONS_Printf(M_GetText("Found, "));
}
else
{
i = SL_SearchServer(servernode);
if (i < 0)
break; // the case
}
// Quit here rather than downloading files and being refused later.
if (serverlist[i].info.numberofplayer >= serverlist[i].info.maxplayer)
{
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING);
return;
}
if (!server)
{
D_ParseFileneeded(serverlist[i].info.fileneedednum,
serverlist[i].info.fileneeded);
CONS_Printf(M_GetText("Checking files...\n"));
i = CL_CheckFiles();
if (i == 2) // cannot join for some reason
{
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(M_GetText(
"You have WAD files loaded or have\n"
"modified the game in some way, and\n"
"your file list does not match\n"
"the server's file list.\n"
"Please restart SRB2 before connecting.\n\n"
"Press ESC\n"
), NULL, MM_NOTHING);
return;
}
else if (i == 1)
cl_mode = cl_askjoin;
else
{
// must download something
// can we, though?
if (!CL_CheckDownloadable()) // nope!
{
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(M_GetText(
"You cannot conect to this server\n"
"because you cannot download the files\n"
"that you are missing from the server.\n\n"
"See the console or log file for\n"
"more details.\n\n"
"Press ESC\n"
), NULL, MM_NOTHING);
return;
}
// no problem if can't send packet, we will retry later
if (CL_SendRequestFile())
cl_mode = cl_downloadfiles;
}
}
else
cl_mode = cl_askjoin; // files need not be checked for the server.
break;
}
// ask the info to the server (askinfo packet)
if (asksent + NEWTICRATE < I_GetTime())
{
SendAskInfo(servernode, viams);
asksent = I_GetTime();
}
#else
(void)viams;
// No netgames, so we skip this state.
cl_mode = cl_askjoin;
#endif // ifndef NONET/else
// NET TODO
break;
case cl_downloadfiles:
waitmore = false;
......@@ -1860,27 +411,10 @@ static void CL_ConnectToServer(boolean viams)
cl_mode = cl_askjoin; // don't break case continue to cljoin request now
case cl_askjoin:
CL_LoadServerFiles();
#ifdef JOININGAME
// prepare structures to save the file
// WARNING: this can be useless in case of server not in GS_LEVEL
// but since the network layer doesn't provide ordered packets...
CL_PrepareDownloadSaveGame(tmpsave);
#endif
if (CL_SendJoin())
cl_mode = cl_waitjoinresponse;
//CL_LoadServerFiles();
if (CL_SendJoin()) // Send join request, server instantly connects.
cl_mode = server ? cl_connected : cl_waitjoinresponse;
break;
#ifdef JOININGAME
case cl_downloadsavegame:
if (fileneeded[0].status == FS_FOUND)
{
// Gamestate is now handled within CL_LoadReceivedSavegame()
CL_LoadReceivedSavegame();
cl_mode = cl_connected;
} // don't break case continue to cl_connected
else
break;
#endif
case cl_waitjoinresponse:
case cl_connected:
default:
......@@ -1892,7 +426,6 @@ static void CL_ConnectToServer(boolean viams)
return;
}
GetPackets();
Net_AckTicker();
// call it only one by tic
......@@ -1950,165 +483,8 @@ static void CL_ConnectToServer(boolean viams)
}
#ifndef NONET
typedef struct banreason_s
{
char *reason;
struct banreason_s *prev; //-1
struct banreason_s *next; //+1
} banreason_t;
static banreason_t *reasontail = NULL; //last entry, use prev
static banreason_t *reasonhead = NULL; //1st entry, use next
static void Command_ShowBan(void) //Print out ban list
static void Command_connect(void)
{
size_t i;
const char *address, *mask;
banreason_t *reasonlist = reasonhead;
if (I_GetBanAddress)
CONS_Printf(M_GetText("Ban List:\n"));
else
return;
for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
{
if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
CONS_Printf("%s: %s ", sizeu1(i+1), address);
else
CONS_Printf("%s: %s/%s ", sizeu1(i+1), address, mask);
if (reasonlist && reasonlist->reason)
CONS_Printf("(%s)\n", reasonlist->reason);
else
CONS_Printf("\n");
if (reasonlist) reasonlist = reasonlist->next;
}
if (i == 0 && !address)
CONS_Printf(M_GetText("(empty)\n"));
}
void D_SaveBan(void)
{
FILE *f;
size_t i;
banreason_t *reasonlist = reasonhead;
const char *address, *mask;
if (!reasonhead)
return;
f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "w");
if (!f)
{
CONS_Alert(CONS_WARNING, M_GetText("Could not save ban list into ban.txt\n"));
return;
}
for (i = 0;(address = I_GetBanAddress(i)) != NULL;i++)
{
if (!I_GetBanMask || (mask = I_GetBanMask(i)) == NULL)
fprintf(f, "%s 0", address);
else
fprintf(f, "%s %s", address, mask);
if (reasonlist && reasonlist->reason)
fprintf(f, " %s\n", reasonlist->reason);
else
fprintf(f, " %s\n", "NA");
if (reasonlist) reasonlist = reasonlist->next;
}
fclose(f);
}
static void Ban_Add(const char *reason)
{
banreason_t *reasonlist = malloc(sizeof(*reasonlist));
if (!reasonlist)
return;
if (!reason)
reason = "NA";
reasonlist->next = NULL;
reasonlist->reason = Z_StrDup(reason);
if ((reasonlist->prev = reasontail) == NULL)
reasonhead = reasonlist;
else
reasontail->next = reasonlist;
reasontail = reasonlist;
}
static void Command_ClearBans(void)
{
banreason_t *temp;
if (!I_ClearBans)
return;
I_ClearBans();
reasontail = NULL;
while (reasonhead)
{
temp = reasonhead->next;
Z_Free(reasonhead->reason);
free(reasonhead);
reasonhead = temp;
}
}
static void Ban_Load_File(boolean warning)
{
FILE *f;
size_t i;
const char *address, *mask;
char buffer[MAX_WADPATH];
f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r");
if (!f)
{
if (warning)
CONS_Alert(CONS_WARNING, M_GetText("Could not open ban.txt for ban list\n"));
return;
}
if (I_ClearBans)
Command_ClearBans();
else
{
fclose(f);
return;
}
for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++)
{
address = strtok(buffer, " \t\r\n");
mask = strtok(NULL, " \t\r\n");
I_SetBanAddress(address, mask);
Ban_Add(strtok(NULL, "\r\n"));
}
fclose(f);
}
static void Command_ReloadBan(void) //recheck ban.txt
{
Ban_Load_File(true);
}
static void Command_connect(void)
{
// Assume we connect directly.
boolean viams = false;
if (COM_Argc() < 2)
{
CONS_Printf(M_GetText(
......@@ -2129,57 +505,29 @@ static void Command_connect(void)
server = false;
if (!stricmp(COM_Argv(1), "self"))
if (netgame)
{
servernode = 0;
server = true;
/// \bug should be but...
//SV_SpawnServer();
CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n"));
return;
}
else
{
// used in menu to connect to a server in the list
if (netgame && !stricmp(COM_Argv(1), "node"))
{
servernode = (SINT8)atoi(COM_Argv(2));
// Use MS to traverse NAT firewalls.
viams = true;
}
else if (netgame)
{
CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n"));
boolean success = false;
if (COM_Argc() >= 3)
success = D_NetConnect(COM_Argv(1), COM_Argv(2));
else
success = D_NetConnect(COM_Argv(1), NULL);
if (!success) {
M_StartMessage(M_GetText("Failed to connect to server.\n\nPress ESC\n"), NULL, MM_NOTHING);
return;
}
else if (I_NetOpenSocket)
{
MSCloseUDPSocket(); // Tidy up before wiping the slate.
I_NetOpenSocket();
netgame = true;
multiplayer = true;
if (!stricmp(COM_Argv(1), "any"))
servernode = BROADCASTADDR;
else if (I_NetMakeNodewPort && COM_Argc() >= 3)
servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2));
else if (I_NetMakeNodewPort)
servernode = I_NetMakeNode(COM_Argv(1));
else
{
CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n"));
D_CloseConnection();
return;
}
}
else
CONS_Alert(CONS_ERROR, M_GetText("There is no network driver\n"));
}
splitscreen = false;
SplitScreen_OnChange();
botingame = false;
botskin = 0;
CL_ConnectToServer(viams);
CL_ConnectToServer();
}
#endif
......@@ -2201,6 +549,7 @@ void CL_ClearPlayer(INT32 playernum)
}
players[playernum].mo = NULL;
memset(&players[playernum], 0, sizeof (player_t));
sprintf(player_names[playernum], "New Player %u", playernum+1);
}
//
......@@ -2208,7 +557,7 @@ void CL_ClearPlayer(INT32 playernum)
//
// Removes a player from the current game
//
static void CL_RemovePlayer(INT32 playernum)
void CL_RemovePlayer(INT32 playernum)
{
// Sanity check: exceptional cases (i.e. c-fails) can cause multiple
// kick commands to be issued for the same player.
......@@ -2221,9 +570,6 @@ static void CL_RemovePlayer(INT32 playernum)
playerpernode[node]--;
if (playerpernode[node] <= 0)
{
// If a resynch was in progress, well, it no longer needs to be.
SV_InitResynchVars(playernode[playernum]);
nodeingame[playernode[playernum]] = false;
Net_CloseConnection(playernode[playernum]);
ResetNode(node);
......@@ -2269,11 +615,7 @@ static void CL_RemovePlayer(INT32 playernum)
// remove avatar of player
playeringame[playernum] = false;
playernode[playernum] = UINT8_MAX;
while (!playeringame[doomcom->numslots-1] && doomcom->numslots > 1)
doomcom->numslots--;
// Reset the name
sprintf(player_names[playernum], "Player %d", playernum+1);
net_playercount--;
if (playernum == adminplayer)
adminplayer = -1; // don't stay admin after you're gone
......@@ -2312,8 +654,6 @@ void CL_Reset(void)
multiplayer = false;
servernode = 0;
server = true;
doomcom->numnodes = 1;
doomcom->numslots = 1;
SV_StopServer();
SV_ResetServer();
......@@ -2376,7 +716,6 @@ static void Command_Nodes(void)
{
INT32 i;
size_t maxlen = 0;
const char *address;
for (i = 0; i < MAXPLAYERS; i++)
{
......@@ -2391,8 +730,7 @@ static void Command_Nodes(void)
{
CONS_Printf("%.2u: %*s", i, (int)maxlen, player_names[i]);
CONS_Printf(" - %.2d", playernode[i]);
if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL)
CONS_Printf(" - %s", address);
// NET TODO: show network addresses
if (i == adminplayer)
CONS_Printf(M_GetText(" (verified admin)"));
......@@ -2418,45 +756,18 @@ static void Command_Ban(void)
XBOXSTATIC UINT8 buf[3 + MAX_REASONLENGTH];
UINT8 *p = buf;
const SINT8 pn = nametonum(COM_Argv(1));
const INT32 node = playernode[(INT32)pn];
//const INT32 node = playernode[(INT32)pn];
if (pn == -1 || pn == 0)
return;
else
WRITEUINT8(p, pn);
if (I_Ban && !I_Ban(node))
// NET TODO
{
CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
//CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
WRITEUINT8(p, KICK_MSG_GO_AWAY);
SendNetXCmd(XD_KICK, &buf, 2);
}
else
{
Ban_Add(COM_Argv(2));
if (COM_Argc() == 2)
{
WRITEUINT8(p, KICK_MSG_BANNED);
SendNetXCmd(XD_KICK, &buf, 2);
}
else
{
size_t i, j = COM_Argc();
char message[MAX_REASONLENGTH];
//Steal from the motd code so you don't have to put the reason in quotes.
strlcpy(message, COM_Argv(2), sizeof message);
for (i = 3; i < j; i++)
{
strlcat(message, " ", sizeof message);
strlcat(message, COM_Argv(i), sizeof message);
}
WRITEUINT8(p, KICK_MSG_CUSTOM_BAN);
WRITESTRINGN(p, message, MAX_REASONLENGTH);
SendNetXCmd(XD_KICK, &buf, p - buf);
}
}
}
else
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
......@@ -2574,7 +885,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
nodetoplayer2[playernode[pnum]]);
*/
pnum = playernum;
msg = KICK_MSG_CON_FAIL;
msg = KICK_MSG_STOP_HACKING;
}
CONS_Printf("\x82%s ", player_names[pnum]);
......@@ -2583,10 +894,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
// If the playernum isn't zero (the server) then the server needs to record the ban.
if (server && playernum && msg == KICK_MSG_BANNED)
{
if (I_Ban && !I_Ban(playernode[(INT32)pnum]))
{
CONS_Alert(CONS_WARNING, M_GetText("Too many bans! Geez, that's a lot of people you're excluding...\n"));
}
// NET TODO
}
switch (msg)
......@@ -2594,50 +902,18 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
case KICK_MSG_GO_AWAY:
CONS_Printf(M_GetText("has been kicked (Go away)\n"));
break;
#ifdef NEWPING
case KICK_MSG_PING_HIGH:
CONS_Printf(M_GetText("left the game (Broke ping limit)\n"));
break;
#endif
case KICK_MSG_CON_FAIL:
CONS_Printf(M_GetText("left the game (Synch failure)\n"));
if (M_CheckParm("-consisdump")) // Helps debugging some problems
{
INT32 i;
CONS_Printf(M_GetText("Player kicked is #%d, dumping consistency...\n"), pnum);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
CONS_Printf("-------------------------------------\n");
CONS_Printf("Player %d: %s\n", i, player_names[i]);
CONS_Printf("Skin: %d\n", players[i].skin);
CONS_Printf("Color: %d\n", players[i].skincolor);
CONS_Printf("Speed: %d\n",players[i].speed>>FRACBITS);
if (players[i].mo)
{
if (!players[i].mo->skin)
CONS_Printf("Mobj skin: NULL!\n");
else
CONS_Printf("Mobj skin: %s\n", ((skin_t *)players[i].mo->skin)->name);
CONS_Printf("Position: %d, %d, %d\n", players[i].mo->x, players[i].mo->y, players[i].mo->z);
if (!players[i].mo->state)
CONS_Printf("State: S_NULL\n");
else
CONS_Printf("State: %d\n", (statenum_t)(players[i].mo->state-states));
}
else
CONS_Printf("Mobj: NULL\n");
CONS_Printf("-------------------------------------\n");
}
}
case KICK_MSG_STOP_HACKING:
CONS_Printf(M_GetText("left the game (Hack attempted)\n"));
break;
case KICK_MSG_TIMEOUT:
CONS_Printf(M_GetText("left the game (Connection timeout)\n"));
break;
case KICK_MSG_XD_FAIL:
CONS_Printf(M_GetText("left the game (Command buffer error)\n"));
break;
case KICK_MSG_PLAYER_QUIT:
if (netgame) // not splitscreen/bots
CONS_Printf(M_GetText("left the game\n"));
......@@ -2657,18 +933,15 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
if (pnum == consoleplayer)
{
#ifdef DUMPCONSISTENCY
if (msg == KICK_MSG_CON_FAIL) SV_SavedGame();
#endif
D_QuitNetGame();
CL_Reset();
D_StartTitle();
if (msg == KICK_MSG_CON_FAIL)
M_StartMessage(M_GetText("Server closed connection\n(synch failure)\nPress ESC\n"), NULL, MM_NOTHING);
#ifdef NEWPING
if (msg == KICK_MSG_STOP_HACKING) // You shouldn't have done that.
M_StartMessage(M_GetText("Server closed connection\n\nPress ESC\n"), NULL, MM_NOTHING);
else if (msg == KICK_MSG_PING_HIGH)
M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING);
#endif
else if (msg == KICK_MSG_XD_FAIL)
M_StartMessage(M_GetText("Server closed connection\n(Command buffer error)\nPress ESC\n"), NULL, MM_NOTHING);
else if (msg == KICK_MSG_BANNED)
M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING);
else if (msg == KICK_MSG_CUSTOM_KICK)
......@@ -2684,11 +957,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL };
consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done
static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}};
consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t resynchattempts_cons_t[] = {{0, "MIN"}, {20, "MAX"}, {0, NULL}};
consvar_t cv_resynchattempts = {"resynchattempts", "10", 0, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL };
consvar_t cv_blamecfail = {"blamecfail", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL };
static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}};
consvar_t cv_maxplayers = {"maxplayers", "12", CV_SAVE, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
// max file size to send to a player (in kilobytes)
static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {51200, "MAX"}, {0, NULL}};
......@@ -2706,9 +976,6 @@ void D_ClientServerInit(void)
COM_AddCommand("getplayernum", Command_GetPlayerNum);
COM_AddCommand("kick", Command_Kick);
COM_AddCommand("ban", Command_Ban);
COM_AddCommand("clearbans", Command_ClearBans);
COM_AddCommand("showbanlist", Command_ShowBan);
COM_AddCommand("reloadbans", Command_ReloadBan);
COM_AddCommand("connect", Command_connect);
COM_AddCommand("nodes", Command_Nodes);
#endif
......@@ -2719,12 +986,6 @@ void D_ClientServerInit(void)
CV_RegisterVar(&cv_allownewplayer);
CV_RegisterVar(&cv_joinnextround);
CV_RegisterVar(&cv_showjoinaddress);
CV_RegisterVar(&cv_resynchattempts);
CV_RegisterVar(&cv_blamecfail);
#ifdef DUMPCONSISTENCY
CV_RegisterVar(&cv_dumpconsistency);
#endif
Ban_Load_File(false);
#endif
gametic = 0;
......@@ -2752,22 +1013,9 @@ void SV_ResetServer(void)
{
INT32 i;
// +1 because this command will be executed in com_executebuffer in
// tryruntic so gametic will be incremented, anyway maketic > gametic
// is not a issue
maketic = gametic + 1;
neededtic = maketic;
tictoclear = maketic;
for (i = 0; i < MAXNETNODES; i++)
{
ResetNode(i);
// Make sure resynch status doesn't get carried over!
SV_InitResynchVars(i);
}
for (i = 0; i < MAXPLAYERS; i++)
{
#ifdef HAVE_BLUA
......@@ -2792,7 +1040,8 @@ void SV_ResetServer(void)
if (server)
servernode = 0;
doomcom->numslots = 0;
net_nodecount = 0;
net_playercount = 0;
// clear server_context
memset(server_context, '-', 8);
......@@ -2822,7 +1071,7 @@ static inline void SV_GenContext(void)
//
void D_QuitNetGame(void)
{
if (!netgame || !netbuffer)
if (!netgame)
return;
DEBFILE("===========================================================================\n"
......@@ -2832,23 +1081,6 @@ void D_QuitNetGame(void)
// abort send/receive of files
CloseNetFile();
if (server)
{
INT32 i;
netbuffer->packettype = PT_SERVERSHUTDOWN;
for (i = 0; i < MAXNETNODES; i++)
if (nodeingame[i])
HSendPacket(i, true, 0, 0);
if (serverrunning && ms_RoomId > 0)
UnregisterServer();
}
else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]!=0)
{
netbuffer->packettype = PT_CLIENTQUIT;
HSendPacket(servernode, true, 0, 0);
}
D_CloseConnection();
adminplayer = -1;
......@@ -2881,7 +1113,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
INT16 node, newplayernum;
boolean splitscreenplayer;
if (playernum != serverplayer && playernum != adminplayer)
if (playernum != serverplayer)
{
// protect against hacked/buggy client
CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]);
......@@ -2890,7 +1122,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
......@@ -2907,8 +1139,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
CL_ClearPlayer(newplayernum);
playeringame[newplayernum] = true;
G_AddPlayer(newplayernum);
if (newplayernum+1 > doomcom->numslots)
doomcom->numslots = (INT16)(newplayernum+1);
net_playercount++;
if (netgame)
CONS_Printf(M_GetText("Player %d has joined the game (node %d)\n"), newplayernum+1, node);
......@@ -2937,1038 +1168,168 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
if (botingame)
players[newplayernum].bot = 1;
// Same goes for player 2 when relevant
players[newplayernum].pflags &= ~(PF_FLIPCAM|PF_ANALOGMODE);
if (cv_flipcam2.value)
players[newplayernum].pflags |= PF_FLIPCAM;
if (cv_analog2.value)
players[newplayernum].pflags |= PF_ANALOGMODE;
}
D_SendPlayerConfig();
addedtogame = true;
}
else if (server && netgame && cv_showjoinaddress.value)
{
const char *address;
if (I_GetNodeAddress && (address = I_GetNodeAddress(node)) != NULL)
CONS_Printf(M_GetText("Player Address is %s\n"), address);
}
if (server && multiplayer && motd[0] != '\0')
COM_BufAddText(va("sayto %d %s\n", newplayernum, motd));
#ifdef HAVE_BLUA
LUAh_PlayerJoin(newplayernum);
#endif
}
static boolean SV_AddWaitingPlayers(void)
{
INT32 node, n, newplayer = false;
XBOXSTATIC UINT8 buf[2];
UINT8 newplayernum = 0;
// What is the reason for this? Why can't newplayernum always be 0?
if (dedicated)
newplayernum = 1;
for (node = 0; node < MAXNETNODES; node++)
{
// splitscreen can allow 2 player in one node
for (; nodewaiting[node] > 0; nodewaiting[node]--)
{
newplayer = true;
// search for a free playernum
// we can't use playeringame since it is not updated here
for (; newplayernum < MAXPLAYERS; newplayernum++)
{
for (n = 0; n < MAXNETNODES; n++)
if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum)
break;
if (n == MAXNETNODES)
break;
}
// should never happen since we check the playernum
// before accepting the join
I_Assert(newplayernum < MAXPLAYERS);
playernode[newplayernum] = (UINT8)node;
buf[0] = (UINT8)node;
buf[1] = newplayernum;
if (playerpernode[node] < 1)
nodetoplayer[node] = newplayernum;
else
{
nodetoplayer2[node] = newplayernum;
buf[1] |= 0x80;
}
playerpernode[node]++;
SendNetXCmd(XD_ADDPLAYER, &buf, 2);
DEBFILE(va("Server added player %d node %d\n", newplayernum, node));
// use the next free slot (we can't put playeringame[newplayernum] = true here)
newplayernum++;
}
}
return newplayer;
}
void CL_AddSplitscreenPlayer(void)
{
if (cl_mode == cl_connected)
CL_SendJoin();
}
void CL_RemoveSplitscreenPlayer(void)
{
XBOXSTATIC UINT8 buf[2];
if (cl_mode != cl_connected)
return;
buf[0] = (UINT8)secondarydisplayplayer;
buf[1] = KICK_MSG_PLAYER_QUIT;
SendNetXCmd(XD_KICK, &buf, 2);
}
// is there a game running
boolean Playing(void)
{
return (server && serverrunning) || (!server && cl_mode == cl_connected);
}
boolean SV_SpawnServer(void)
{
if (demoplayback)
G_StopDemo(); // reset engine parameter
if (metalplayback)
G_StopMetalDemo();
if (!serverrunning)
{
CONS_Printf(M_GetText("Starting Server....\n"));
serverrunning = true;
SV_ResetServer();
SV_GenContext();
if (netgame && I_NetOpenSocket)
{
MSCloseUDPSocket(); // Tidy up before wiping the slate.
I_NetOpenSocket();
if (ms_RoomId > 0)
RegisterServer();
}
// non dedicated server just connect to itself
if (!dedicated)
CL_ConnectToServer(false);
else doomcom->numslots = 1;
}
return SV_AddWaitingPlayers();
}
void SV_StopServer(void)
{
tic_t i;
if (gamestate == GS_INTERMISSION)
Y_EndIntermission();
gamestate = wipegamestate = GS_NULL;
localtextcmd[0] = 0;
localtextcmd2[0] = 0;
for (i = 0; i < BACKUPTICS; i++)
D_Clearticcmd(i);
consoleplayer = 0;
cl_mode = cl_searching;
maketic = gametic+1;
neededtic = maketic;
serverrunning = false;
}
// called at singleplayer start and stopdemo
void SV_StartSinglePlayerServer(void)
{
server = true;
netgame = false;
multiplayer = false;
gametype = GT_COOP;
// no more tic the game with this settings!
SV_StopServer();
if (splitscreen)
multiplayer = true;
}
static void SV_SendRefuse(INT32 node, const char *reason)
{
strcpy(netbuffer->u.serverrefuse.reason, reason);
netbuffer->packettype = PT_SERVERREFUSE;
HSendPacket(node, true, 0, strlen(netbuffer->u.serverrefuse.reason) + 1);
Net_CloseConnection(node);
}
// used at txtcmds received to check packetsize bound
static size_t TotalTextCmdPerTic(tic_t tic)
{
INT32 i;
size_t total = 1; // num of textcmds in the tic (ntextcmd byte)
for (i = 0; i < MAXPLAYERS; i++)
{
UINT8 *textcmd = D_GetExistingTextcmd(tic, i);
if ((!i || playeringame[i]) && textcmd)
total += 2 + textcmd[0]; // "+2" for size and playernum
}
return total;
}
static void HandleConnect(SINT8 node)
{
if (bannednode && bannednode[node])
SV_SendRefuse(node, M_GetText("You have been banned\nfrom the server"));
else if (netbuffer->u.clientcfg.version != VERSION
|| netbuffer->u.clientcfg.subversion != SUBVERSION)
SV_SendRefuse(node, va(M_GetText("Different SRB2 versions cannot\nplay a netgame!\n(server version %d.%d.%d)"), VERSION/100, VERSION%100, SUBVERSION));
else if (!cv_allownewplayer.value && node)
SV_SendRefuse(node, M_GetText("The server is not accepting\njoins for the moment"));
else if (D_NumPlayers() >= cv_maxplayers.value)
SV_SendRefuse(node, va(M_GetText("Maximum players reached: %d"), cv_maxplayers.value));
else if (netgame && netbuffer->u.clientcfg.localplayers > 1) // Hacked client?
SV_SendRefuse(node, M_GetText("Too many players from\nthis node."));
else if (netgame && !netbuffer->u.clientcfg.localplayers) // Stealth join?
SV_SendRefuse(node, M_GetText("No players from\nthis node."));
else
{
#ifndef NONET
boolean newnode = false;
#endif
// client authorised to join
nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]);
if (!nodeingame[node])
{
gamestate_t backupstate = gamestate;
#ifndef NONET
newnode = true;
#endif
SV_AddNode(node);
// you get a free second before desynch checks. use it wisely.
SV_InitResynchVars(node);
if (cv_joinnextround.value && gameaction == ga_nothing)
G_SetGamestate(GS_WAITINGPLAYERS);
if (!SV_SendServerConfig(node))
{
G_SetGamestate(backupstate);
ResetNode(node);
SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
/// \todo fix this !!!
return; // restart the while
}
//if (gamestate != GS_LEVEL) // GS_INTERMISSION, etc?
// SV_SendPlayerConfigs(node); // send bare minimum player info
G_SetGamestate(backupstate);
DEBFILE("new node joined\n");
}
#ifdef JOININGAME
if (nodewaiting[node])
{
if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode)
{
SV_SendSaveGame(node); // send a complete game state
DEBFILE("send savegame\n");
}
SV_AddWaitingPlayers();
player_joining = true;
}
#else
#ifndef NONET
// I guess we have no use for this if we aren't doing mid-level joins?
(void)newnode;
#endif
#endif
}
}
static void HandleShutdown(SINT8 node)
{
(void)node;
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING);
}
static void HandleTimeout(SINT8 node)
{
(void)node;
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(M_GetText("Server Timeout\n\nPress Esc\n"), NULL, MM_NOTHING);
}
#ifndef NONET
static void HandleServerInfo(SINT8 node)
{
// compute ping in ms
const tic_t ticnow = I_GetTime();
const tic_t ticthen = (tic_t)LONG(netbuffer->u.serverinfo.time);
const tic_t ticdiff = (ticnow - ticthen)*1000/NEWTICRATE;
netbuffer->u.serverinfo.time = (tic_t)LONG(ticdiff);
netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0;
SL_InsertServer(&netbuffer->u.serverinfo, node);
}
#endif
/** \brief GetPackets
\todo break this 300 line function into multiple functions
*/
static void GetPackets(void)
{FILESTAMP
XBOXSTATIC INT32 netconsole;
XBOXSTATIC SINT8 node;
XBOXSTATIC tic_t realend,realstart;
XBOXSTATIC UINT8 *pak, *txtpak, numtxtpak;
FILESTAMP
player_joining = false;
while (HGetPacket())
{
node = (SINT8)doomcom->remotenode;
if (netbuffer->packettype == PT_CLIENTJOIN && server)
{
HandleConnect(node);
continue;
}
if (netbuffer->packettype == PT_SERVERSHUTDOWN && node == servernode
&& !server && cl_mode != cl_searching)
{
HandleShutdown(node);
continue;
}
if (netbuffer->packettype == PT_NODETIMEOUT && node == servernode
&& !server && cl_mode != cl_searching)
{
HandleTimeout(node);
continue;
}
#ifndef NONET
if (netbuffer->packettype == PT_SERVERINFO)
{
HandleServerInfo(node);
continue;
}
#endif
if (netbuffer->packettype == PT_PLAYERINFO)
continue; // We do nothing with PLAYERINFO, that's for the MS browser.
if (!nodeingame[node])
{
if (node != servernode)
DEBFILE(va("Received packet from unknown host %d\n", node));
// anyone trying to join
switch (netbuffer->packettype)
{
case PT_ASKINFOVIAMS:
if (server && serverrunning)
{
INT32 clientnode = I_NetMakeNode(netbuffer->u.msaskinfo.clientaddr);
SV_SendServerInfo(clientnode, (tic_t)LONG(netbuffer->u.msaskinfo.time));
SV_SendPlayerInfo(clientnode); // send extra info
Net_CloseConnection(clientnode);
// Don't close connection to MS.
}
break;
case PT_ASKINFO:
if (server && serverrunning)
{
SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time));
SV_SendPlayerInfo(node); // send extra info
Net_CloseConnection(node);
}
break;
case PT_SERVERREFUSE: // negative response of client join request
if (server && serverrunning)
{ // but wait I thought I'm the server?
Net_CloseConnection(node);
break;
}
if (cl_mode == cl_waitjoinresponse)
{
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
netbuffer->u.serverrefuse.reason), NULL, MM_NOTHING);
// Will be reset by caller. Signals refusal.
cl_mode = cl_aborted;
}
break;
case PT_SERVERCFG: // positive response of client join request
{
INT32 j;
UINT8 *scp;
if (server && serverrunning && node != servernode)
{ // but wait I thought I'm the server?
Net_CloseConnection(node);
break;
}
/// \note how would this happen? and is it doing the right thing if it does?
if (cl_mode != cl_waitjoinresponse)
break;
if (!server)
{
maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
gametype = netbuffer->u.servercfg.gametype;
modifiedgame = netbuffer->u.servercfg.modifiedgame;
adminplayer = netbuffer->u.servercfg.adminplayer;
memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
}
nodeingame[(UINT8)servernode] = true;
serverplayer = netbuffer->u.servercfg.serverplayer;
doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
mynode = netbuffer->u.servercfg.clientnode;
if (serverplayer >= 0)
playernode[(UINT8)serverplayer] = servernode;
if (netgame)
#ifdef JOININGAME
CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n"));
#else
CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n"));
#endif
DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode));
memset(playeringame, 0, sizeof(playeringame));
for (j = 0; j < MAXPLAYERS; j++)
{
if (netbuffer->u.servercfg.playerskins[j] == 0xFF
&& netbuffer->u.servercfg.playercolor[j] == 0xFF)
continue; // not in game
playeringame[j] = true;
SetPlayerSkinByNum(j, (INT32)netbuffer->u.servercfg.playerskins[j]);
players[j].skincolor = netbuffer->u.servercfg.playercolor[j];
}
scp = netbuffer->u.servercfg.varlengthinputs;
CV_LoadPlayerNames(&scp);
CV_LoadNetVars(&scp);
#ifdef JOININGAME
if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* ||
netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
cl_mode = cl_downloadsavegame;
else
#endif
cl_mode = cl_connected;
break;
}
// handled in d_netfil.c
case PT_FILEFRAGMENT:
if (server)
{ // but wait I thought I'm the server?
Net_CloseConnection(node);
break;
}
else
Got_Filetxpak();
break;
case PT_REQUESTFILE:
if (server)
Got_RequestFilePak(node);
break;
case PT_NODETIMEOUT:
case PT_CLIENTQUIT:
if (server)
Net_CloseConnection(node);
break;
case PT_CLIENTCMD:
break; // this is not an "unknown packet"
case PT_SERVERTICS:
// do not remove my own server (we have just get a out of order packet)
if (node == servernode)
break;
default:
DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
Net_CloseConnection(node);
break; // ignore it
} // switch
continue; //while
}
if (dedicated && node == 0) netconsole = 0;
else netconsole = nodetoplayer[node];
#ifdef PARANOIA
if (netconsole >= MAXPLAYERS)
I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
#endif
txtpak = NULL;
switch (netbuffer->packettype)
{
// -------------------------------------------- SERVER RECEIVE ----------
case PT_RESYNCHGET:
SV_AcknowledgeResynchAck(netconsole, netbuffer->u.resynchgot);
break;
case PT_CLIENTCMD:
case PT_CLIENT2CMD:
case PT_CLIENTMIS:
case PT_CLIENT2MIS:
case PT_NODEKEEPALIVE:
case PT_NODEKEEPALIVEMIS:
if (!server)
break;
// ignore tics from those not synched
if (resynch_inprogress[node])
break;
// to save bytes, only the low byte of tic numbers are sent
// Figure out what the rest of the bytes are
realstart = ExpandTics(netbuffer->u.clientpak.client_tic);
realend = ExpandTics(netbuffer->u.clientpak.resendfrom);
if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
|| netbuffer->packettype == PT_NODEKEEPALIVEMIS
|| supposedtics[node] < realend)
{
supposedtics[node] = realend;
}
// discard out of order packet
if (nettics[node] > realend)
{
DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node]));
break;
}
// update the nettics
nettics[node] = realend;
// don't do anything for packets of type NODEKEEPALIVE?
if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
|| netbuffer->packettype == PT_NODEKEEPALIVEMIS)
break;
// copy ticcmd
G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
// check ticcmd for "speed hacks"
if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
|| netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
{
XBOXSTATIC char buf[2];
CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value recieved from node %d\n"), netconsole);
//D_Clearticcmd(k);
buf[0] = (char)netconsole;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
break;
}
// splitscreen cmd
if (netbuffer->packettype == PT_CLIENT2CMD && nodetoplayer2[node] >= 0)
G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
&netbuffer->u.client2pak.cmd2, 1);
// a delay before we check resynching
// used on join or just after a synch fail
if (resynch_delay[node])
{
--resynch_delay[node];
break;
}
// check player consistancy during the level
if (realstart <= gametic && realstart > gametic - BACKUPTICS+1 && gamestate == GS_LEVEL
&& consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy))
{
SV_RequireResynch(node);
if (cv_resynchattempts.value && resynch_score[node] <= (unsigned)cv_resynchattempts.value*250)
{
if (cv_blamecfail.value)
CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
netconsole+1, player_names[netconsole],
consistancy[realstart%BACKUPTICS],
SHORT(netbuffer->u.clientpak.consistancy));
DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n",
netconsole, realstart, consistancy[realstart%BACKUPTICS],
SHORT(netbuffer->u.clientpak.consistancy)));
break;
}
else
{
XBOXSTATIC UINT8 buf[3];
buf[0] = (UINT8)netconsole;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
netconsole, realstart, consistancy[realstart%BACKUPTICS],
SHORT(netbuffer->u.clientpak.consistancy)));
break;
}
}
else if (resynch_score[node])
--resynch_score[node];
break;
case PT_TEXTCMD2: // splitscreen special
netconsole = nodetoplayer2[node];
case PT_TEXTCMD:
if (!server)
break;
if (netconsole < 0 || netconsole >= MAXPLAYERS)
Net_UnAcknowledgPacket(node);
else
{
size_t j;
tic_t tic = maketic;
UINT8 *textcmd;
// check if tic that we are making isn't too large else we cannot send it :(
// doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time
j = software_MAXPACKETLENGTH
- (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE
+ (doomcom->numslots+1)*sizeof(ticcmd_t));
// search a tic that have enougth space in the ticcmd
while ((textcmd = D_GetExistingTextcmd(tic, netconsole)),
(TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD)
&& tic < firstticstosend + BACKUPTICS)
tic++;
if (tic >= firstticstosend + BACKUPTICS)
{
DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, "
"tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)),
maketic, firstticstosend, node, netconsole));
Net_UnAcknowledgPacket(node);
break;
}
// Make sure we have a buffer
if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole);
DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n",
tic, textcmd[0]+1, netconsole, firstticstosend, maketic));
M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
textcmd[0] += (UINT8)netbuffer->u.textcmd[0];
}
break;
case PT_NODETIMEOUT:
case PT_CLIENTQUIT:
if (!server)
break;
// nodeingame will be put false in the execution of kick command
// this allow to send some packets to the quitting client to have their ack back
nodewaiting[node] = 0;
if (netconsole != -1 && playeringame[netconsole])
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)netconsole;
if (netbuffer->packettype == PT_NODETIMEOUT)
buf[1] = KICK_MSG_TIMEOUT;
else
buf[1] = KICK_MSG_PLAYER_QUIT;
SendNetXCmd(XD_KICK, &buf, 2);
nodetoplayer[node] = -1;
if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0
&& playeringame[(UINT8)nodetoplayer2[node]])
{
buf[0] = nodetoplayer2[node];
SendNetXCmd(XD_KICK, &buf, 2);
nodetoplayer2[node] = -1;
}
}
Net_CloseConnection(node);
nodeingame[node] = false;
break;
// -------------------------------------------- CLIENT RECEIVE ----------
case PT_RESYNCHEND:
// Only accept PT_RESYNCHEND from the server.
if (node != servernode)
{
CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_RESYNCHEND", node);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)node;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
break;
}
resynch_local_inprogress = false;
P_SetRandSeed(netbuffer->u.resynchend.randomseed);
if (gametype == GT_CTF)
resynch_read_ctf(&netbuffer->u.resynchend);
resynch_read_others(&netbuffer->u.resynchend);
break;
case PT_SERVERTICS:
// Only accept PT_SERVERTICS from the server.
if (node != servernode)
{
CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_SERVERTICS", node);
if (server)
{
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)node;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
break;
}
realstart = ExpandTics(netbuffer->u.serverpak.starttic);
realend = realstart + netbuffer->u.serverpak.numtics;
if (!txtpak)
txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots
* netbuffer->u.serverpak.numtics];
if (realend > gametic + BACKUPTICS)
realend = gametic + BACKUPTICS;
cl_packetmissed = realstart > neededtic;
if (realstart <= neededtic && realend > neededtic)
{
tic_t i, j;
pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
for (i = realstart; i < realend; i++)
{
// clear first
D_Clearticcmd(i);
// copy the tics
pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
// copy the textcmds
numtxtpak = *txtpak++;
for (j = 0; j < numtxtpak; j++)
{
INT32 k = *txtpak++; // playernum
const size_t txtsize = txtpak[0]+1;
M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize);
txtpak += txtsize;
}
}
neededtic = realend;
}
else
DEBFILE(va("frame not in bound: %u\n", neededtic));
break;
case PT_RESYNCHING:
// Only accept PT_RESYNCHING from the server.
if (node != servernode)
{
CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_RESYNCHING", node);
if (server)
{
XBOXSTATIC char buf[2];
buf[0] = (char)node;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
break;
}
resynch_local_inprogress = true;
CL_AcknowledgeResynch(&netbuffer->u.resynchpak);
break;
#ifdef NEWPING
case PT_PING:
// Only accept PT_PING from the server.
if (node != servernode)
{
CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_PING", node);
if (server)
{
XBOXSTATIC char buf[2];
buf[0] = (char)node;
buf[1] = KICK_MSG_CON_FAIL;
SendNetXCmd(XD_KICK, &buf, 2);
}
players[newplayernum].pflags &= ~(/*PF_FLIPCAM|*/PF_ANALOGMODE);
//if (cv_flipcam2.value)
//players[newplayernum].pflags |= PF_FLIPCAM;
if (cv_analog2.value)
players[newplayernum].pflags |= PF_ANALOGMODE;
}
D_SendPlayerConfig();
addedtogame = true;
}
else if (server && netgame && cv_showjoinaddress.value)
{
// NET TODO: Show player connection address.
}
break;
}
//Update client ping table from the server.
if (!server)
{
INT32 i;
for (i = 0; i < MAXNETNODES; i++)
if (playeringame[i])
playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i];
}
if (server && multiplayer && motd[0] != '\0')
COM_BufAddText(va("sayto %d %s\n", newplayernum, motd));
break;
#ifdef HAVE_BLUA
LUAh_PlayerJoin(newplayernum);
#endif
case PT_SERVERCFG:
break;
case PT_FILEFRAGMENT:
if (!server)
Got_Filetxpak();
break;
default:
DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n",
netbuffer->packettype, node));
} // end switch
} // end while
}
//
// NetUpdate
// Builds ticcmds for console player,
// sends out a packet
//
// no more use random generator, because at very first tic isn't yet synchronized
// Note: It is called consistAncy on purpose.
//
static INT16 Consistancy(void)
static boolean SV_AddWaitingPlayers(void)
{
INT32 i;
UINT32 ret = 0;
INT32 node, n, newplayer = false;
XBOXSTATIC UINT8 buf[2];
UINT8 newplayernum = 0;
DEBFILE(va("TIC %u ", gametic));
// What is the reason for this? Why can't newplayernum always be 0?
if (dedicated)
newplayernum = 1;
for (i = 0; i < MAXPLAYERS; i++)
for (node = 0; node < MAXNETNODES; node++)
{
if (!playeringame[i])
ret ^= 0xCCCC;
else if (!players[i].mo);
else
// splitscreen can allow 2 player in one node
for (; nodewaiting[node] > 0; nodewaiting[node]--)
{
ret += players[i].mo->x;
ret -= players[i].mo->y;
ret += players[i].powers[pw_shield];
ret *= i+1;
newplayer = true;
// search for a free playernum
// we can't use playeringame since it is not updated here
for (; newplayernum < MAXPLAYERS; newplayernum++)
{
for (n = 0; n < MAXNETNODES; n++)
if (nodetoplayer[n] == newplayernum || nodetoplayer2[n] == newplayernum)
break;
if (n == MAXNETNODES)
break;
}
// should never happen since we check the playernum
// before accepting the join
I_Assert(newplayernum < MAXPLAYERS);
playernode[newplayernum] = (UINT8)node;
buf[0] = (UINT8)node;
buf[1] = newplayernum;
if (playerpernode[node] < 1)
nodetoplayer[node] = newplayernum;
else
{
nodetoplayer2[node] = newplayernum;
buf[1] |= 0x80;
}
playerpernode[node]++;
SendNetXCmd(XD_ADDPLAYER, &buf, 2);
DEBFILE(va("Server added player %d node %d\n", newplayernum, node));
// use the next free slot (we can't put playeringame[newplayernum] = true here)
newplayernum++;
}
}
// I give up
// Coop desynching enemies is painful
if (!G_PlatformGametype())
ret += P_GetRandSeed();
return (INT16)(ret & 0xFFFF);
return newplayer;
}
// send the client packet to the server
static void CL_SendClientCmd(void)
void CL_AddSplitscreenPlayer(void)
{
size_t packetsize = 0;
if (cl_mode == cl_connected)
CL_SendJoin();
}
netbuffer->packettype = PT_CLIENTCMD;
void CL_RemoveSplitscreenPlayer(void)
{
XBOXSTATIC UINT8 buf[2];
if (cl_packetmissed)
netbuffer->packettype++;
netbuffer->u.clientpak.resendfrom = (UINT8)(neededtic & UINT8_MAX);
netbuffer->u.clientpak.client_tic = (UINT8)(gametic & UINT8_MAX);
if (cl_mode != cl_connected)
return;
if (gamestate == GS_WAITINGPLAYERS)
{
// send NODEKEEPALIVE packet
netbuffer->packettype += 4;
packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16);
HSendPacket(servernode, false, 0, packetsize);
}
else if (gamestate != GS_NULL)
{
G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
buf[0] = (UINT8)secondarydisplayplayer;
buf[1] = KICK_MSG_PLAYER_QUIT;
SendNetXCmd(XD_KICK, &buf, 2);
}
// send a special packet with 2 cmd for splitscreen
if (splitscreen || botingame)
{
netbuffer->packettype += 2;
G_MoveTiccmd(&netbuffer->u.client2pak.cmd2, &localcmds2, 1);
packetsize = sizeof (client2cmd_pak);
}
else
packetsize = sizeof (clientcmd_pak);
// is there a game running
boolean Playing(void)
{
return (server && serverrunning) || (!server && cl_mode == cl_connected);
}
HSendPacket(servernode, false, 0, packetsize);
}
boolean SV_SpawnServer(void)
{
if (demoplayback)
G_StopDemo(); // reset engine parameter
if (metalplayback)
G_StopMetalDemo();
if (cl_mode == cl_connected || dedicated)
if (!serverrunning)
{
// send extra data if needed
if (localtextcmd[0])
{
netbuffer->packettype = PT_TEXTCMD;
M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
// all extra data have been sended
if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // send can fail...
localtextcmd[0] = 0;
}
CONS_Printf(M_GetText("Starting Server....\n"));
serverrunning = true;
SV_ResetServer();
SV_GenContext();
if (netgame)
D_NetOpen();
// send extra data if needed for player 2 (splitscreen)
if (localtextcmd2[0])
{
netbuffer->packettype = PT_TEXTCMD2;
M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1);
// all extra data have been sended
if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // send can fail...
localtextcmd2[0] = 0;
}
// non dedicated server just connect to itself
if (!dedicated)
CL_ConnectToServer();
}
return SV_AddWaitingPlayers();
}
// send the server packet
// send tic from firstticstosend to maketic-1
static void SV_SendTics(void)
void SV_StopServer(void)
{
tic_t realfirsttic, lasttictosend, i;
UINT32 n;
INT32 j;
size_t packsize;
UINT8 *bufpos;
UINT8 *ntextcmd;
// send to all client but not to me
// for each node create a packet with x tics and send it
// x is computed using supposedtics[n], max packet size and maketic
for (n = 1; n < MAXNETNODES; n++)
if (nodeingame[n])
{
lasttictosend = maketic;
if (gamestate == GS_INTERMISSION)
Y_EndIntermission();
gamestate = wipegamestate = GS_NULL;
// assert supposedtics[n]>=nettics[n]
realfirsttic = supposedtics[n];
if (realfirsttic >= maketic)
{
// well we have sent all tics we will so use extrabandwidth
// to resent packet that are supposed lost (this is necessary since lost
// packet detection work when we have received packet with firsttic > neededtic
// (getpacket servertics case)
DEBFILE(va("Nothing to send node %u mak=%u sup=%u net=%u \n",
n, maketic, supposedtics[n], nettics[n]));
realfirsttic = nettics[n];
if (realfirsttic >= maketic || (I_GetTime() + n)&3)
// all tic are ok
continue;
DEBFILE(va("Sent %d anyway\n", realfirsttic));
}
if (realfirsttic < firstticstosend)
realfirsttic = firstticstosend;
// compute the length of the packet and cut it if too large
packsize = BASESERVERTICSSIZE;
for (i = realfirsttic; i < lasttictosend; i++)
{
packsize += sizeof (ticcmd_t) * doomcom->numslots;
packsize += TotalTextCmdPerTic(i);
if (packsize > software_MAXPACKETLENGTH)
{
DEBFILE(va("packet too large (%s) at tic %d (should be from %d to %d)\n",
sizeu1(packsize), i, realfirsttic, lasttictosend));
lasttictosend = i;
// too bad: too much player have send extradata and there is too
// much data in one tic.
// To avoid it put the data on the next tic. (see getpacket
// textcmd case) but when numplayer changes the computation can be different
if (lasttictosend == realfirsttic)
{
if (packsize > MAXPACKETLENGTH)
I_Error("Too many players: can't send %s data for %d players to node %d\n"
"Well sorry nobody is perfect....\n",
sizeu1(packsize), doomcom->numslots, n);
else
{
lasttictosend++; // send it anyway!
DEBFILE("sending it anyway\n");
}
}
break;
}
}
D_Clearticcmd();
// Send the tics
netbuffer->packettype = PT_SERVERTICS;
netbuffer->u.serverpak.starttic = (UINT8)realfirsttic;
netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic);
netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots);
bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds;
consoleplayer = 0;
cl_mode = cl_searching;
serverrunning = false;
}
for (i = realfirsttic; i < lasttictosend; i++)
{
bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t));
}
// called at singleplayer start and stopdemo
void SV_StartSinglePlayerServer(void)
{
server = true;
netgame = false;
multiplayer = false;
gametype = GT_COOP;
// add textcmds
for (i = realfirsttic; i < lasttictosend; i++)
{
ntextcmd = bufpos++;
*ntextcmd = 0;
for (j = 0; j < MAXPLAYERS; j++)
{
UINT8 *textcmd = D_GetExistingTextcmd(i, j);
INT32 size = textcmd ? textcmd[0] : 0;
if ((!j || playeringame[j]) && size)
{
(*ntextcmd)++;
WRITEUINT8(bufpos, j);
M_Memcpy(bufpos, textcmd, size + 1);
bufpos += size + 1;
}
}
}
packsize = bufpos - (UINT8 *)&(netbuffer->u);
// no more tic the game with this settings!
SV_StopServer();
HSendPacket(n, false, 0, packsize);
// when tic are too large, only one tic is sent so don't go backward!
if (lasttictosend-doomcom->extratics > realfirsttic)
supposedtics[n] = lasttictosend-doomcom->extratics;
else
supposedtics[n] = lasttictosend;
if (supposedtics[n] < nettics[n]) supposedtics[n] = nettics[n];
}
// node 0 is me!
supposedtics[0] = maketic;
if (splitscreen)
multiplayer = true;
}
//
// NetUpdate
// Builds ticcmds for console player,
// sends out a packet
//
//
// TryRunTics
//
......@@ -3984,64 +1345,32 @@ static void Local_Maketic(INT32 realtics)
if (splitscreen || botingame)
G_BuildTiccmd2(&localcmds2, realtics);
localcmds.angleturn |= TICCMD_RECEIVED;
//localcmds.angleturn |= TICCMD_RECEIVED;
if (addedtogame)
{
G_CopyTiccmd(&players[consoleplayer].cmd, &localcmds, 1);
if (splitscreen || botingame)
G_CopyTiccmd(&players[secondarydisplayplayer].cmd, &localcmds2, 1);
}
}
void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle)
{
tic_t tic;
(void)playernum;
(void)x;
(void)y;
// Revisionist history: adjust the angles in the ticcmds received
// for this player, because they actually preceded the player
// spawning, but will be applied afterwards.
for (tic = server ? maketic : (neededtic - 1); tic >= gametic; tic--)
netcmds[tic%BACKUPTICS][playernum].angleturn = (INT16)((angle>>16) | TICCMD_RECEIVED);
}
// create missed tic
static void SV_Maketic(void)
{
INT32 j;
for (j = 0; j < MAXNETNODES; j++)
if (playerpernode[j])
{
INT32 player = nodetoplayer[j];
if ((netcmds[maketic%BACKUPTICS][player].angleturn & TICCMD_RECEIVED) == 0)
{ // we didn't receive this tic
INT32 i;
DEBFILE(va("MISS tic%4d for node %d\n", maketic, j));
#if defined(PARANOIA) && 0
CONS_Debug(DBG_NETPLAY, "Client Misstic %d\n", maketic);
#endif
// copy the old tic
for (i = 0; i < playerpernode[j]; i++, player = nodetoplayer2[j])
{
netcmds[maketic%BACKUPTICS][player] = netcmds[(maketic-1)%BACKUPTICS][player];
netcmds[maketic%BACKUPTICS][player].angleturn &= ~TICCMD_RECEIVED;
}
}
}
// all tic are now proceed make the next
maketic++;
(void)angle;
// NET TODO: Send everyone a player spawn message??
}
void TryRunTics(tic_t realtics)
{
tic_t i;
// the machine has lagged but it is not so bad
if (realtics > TICRATE/7) // FIXME: consistency failure!!
{
if (server)
realtics = 1;
else
realtics = TICRATE/7;
}
if (realtics > TICRATE/7)
realtics = 1;
if (singletics)
realtics = 1;
......@@ -4055,115 +1384,35 @@ void TryRunTics(tic_t realtics)
NetUpdate();
if (demoplayback)
{
neededtic = gametic + (realtics * cv_playbackspeed.value);
// start a game after a demo
maketic += realtics;
firstticstosend = maketic;
tictoclear = firstticstosend;
}
GetPackets();
#ifdef DEBUGFILE
if (debugfile && (realtics || neededtic > gametic))
if (debugfile && realtics)
{
//SoM: 3/30/2000: Need long INT32 in the format string for args 4 & 5.
//Shut up stupid warning!
fprintf(debugfile, "------------ Tryruntic: REAL:%d NEED:%d GAME:%d LOAD: %d\n",
realtics, neededtic, gametic, debugload);
fprintf(debugfile, "------------ Tryruntic: REAL:%d GAME:%d LOAD: %d\n",
realtics, gametic, debugload);
debugload = 100000;
}
#endif
if (player_joining)
return;
if (neededtic > gametic)
{
if (advancedemo)
D_StartTitle();
else
// run the count * tics
while (neededtic > gametic)
{
DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic));
G_Ticker((gametic % NEWTICRATERATIO) == 0);
ExtraDataTicker();
gametic++;
consistancy[gametic%BACKUPTICS] = Consistancy();
}
}
}
#ifdef NEWPING
static inline void PingUpdate(void)
{
INT32 i;
boolean laggers[MAXPLAYERS];
UINT8 numlaggers = 0;
memset(laggers, 0, sizeof(boolean) * MAXPLAYERS);
netbuffer->packettype = PT_PING;
//check for ping limit breakage.
if (cv_maxping.value)
{
for (i = 1; i < MAXNETNODES; i++)
if (advancedemo)
D_StartTitle();
else
// NET TODO: re-impliment cv_playbackspeed for demos
for (i = 0; i < realtics; i++)
{
if (playeringame[i] && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value))
{
if (players[i].jointime > 30 * TICRATE)
laggers[i] = true;
numlaggers++;
}
}
DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic));
//kick lagging players... unless everyone but the server's ping sucks.
//in that case, it is probably the server's fault.
if (numlaggers < D_NumPlayers() - 1)
{
for (i = 1; i < MAXNETNODES; i++)
{
if (playeringame[i] && laggers[i])
{
XBOXSTATIC char buf[2];
buf[0] = (char)i;
buf[1] = KICK_MSG_PING_HIGH;
SendNetXCmd(XD_KICK, &buf, 2);
}
}
G_Ticker((gametic % NEWTICRATERATIO) == 0);
gametic++;
}
}
//make the ping packet and clear server data for next one
for (i = 0; i < MAXNETNODES; i++)
{
netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount;
//server takes a snapshot of the real ping for display.
//otherwise, pings fluctuate a lot and would be odd to look at.
playerpingtable[i] = realpingtable[i] / pingmeasurecount;
realpingtable[i] = 0; //Reset each as we go.
}
//send out our ping packets
for (i = 0; i < MAXNETNODES; i++)
if (playeringame[i])
HSendPacket(i, true, 0, sizeof(INT32) * MAXPLAYERS);
pingmeasurecount = 1; //Reset count
}
#endif
void NetUpdate(void)
{
static tic_t gametime = 0;
static tic_t resptime = 0;
tic_t nowtime;
INT32 i;
INT32 realtics;
nowtime = I_GetTime();
......@@ -4181,88 +1430,11 @@ void NetUpdate(void)
gametime = nowtime;
if (!(gametime % 255) && netgame && server)
{
#ifdef NEWPING
PingUpdate();
#endif
}
#ifdef NEWPING
if (server)
{
// update node latency values so we can take an average later.
for (i = 0; i < MAXNETNODES; i++)
if (playeringame[i])
realpingtable[i] += G_TicsToMilliseconds(GetLag(i));
pingmeasurecount++;
}
#endif
if (!server)
maketic = neededtic;
Local_Maketic(realtics); // make local tic, and call menu?
if (server)
CL_SendClientCmd(); // send it
FILESTAMP
GetPackets(); // get packet from client or from server
FILESTAMP
// client send the command after a receive of the server
// the server send before because in single player is beter
MasterClient_Ticker(); // acking the master server
if (!server)
{
if (!resynch_local_inprogress)
CL_SendClientCmd(); // send tic cmd
hu_resynching = resynch_local_inprogress;
}
else
{
if (!demoplayback)
{
INT32 counts;
hu_resynching = false;
firstticstosend = gametic;
for (i = 0; i < MAXNETNODES; i++)
if (nodeingame[i] && nettics[i] < firstticstosend)
firstticstosend = nettics[i];
// Don't erase tics not acknowledged
counts = realtics;
for (i = 0; i < MAXNETNODES; ++i)
if (resynch_inprogress[i])
{
SV_SendResynch(i);
counts = -666;
}
// do not make tics while resynching
if (counts != -666)
{
if (maketic + counts >= firstticstosend + BACKUPTICS)
counts = firstticstosend+BACKUPTICS-maketic-1;
for (i = 0; i < counts; i++)
SV_Maketic(); // create missed tics and increment maketic
for (; tictoclear < firstticstosend; tictoclear++) // clear only when acknoledged
D_Clearticcmd(tictoclear); // clear the maketic the new tic
SV_SendTics();
neededtic = maketic; // the server is a client too
}
else
hu_resynching = true;
}
}
Net_AckTicker();
nowtime /= NEWTICRATERATIO;
if (nowtime > resptime)
......@@ -4271,7 +1443,6 @@ FILESTAMP
M_Ticker();
CON_Ticker();
}
FiletxTicker();
}
/** Returns the number of players playing.
......
......@@ -25,56 +25,7 @@
// be transmitted.
// Networking and tick handling related.
#define BACKUPTICS 32
#define MAXTEXTCMD 256
//
// Packet structure
//
typedef enum
{
PT_NOTHING, // To send a nop through the network. ^_~
PT_SERVERCFG, // Server config used in start game
// (must stay 1 for backwards compatibility).
// This is a positive response to a CLIENTJOIN request.
PT_CLIENTCMD, // Ticcmd of the client.
PT_CLIENTMIS, // Same as above with but saying resend from.
PT_CLIENT2CMD, // 2 cmds in the packet for splitscreen.
PT_CLIENT2MIS, // Same as above with but saying resend from
PT_NODEKEEPALIVE, // Same but without ticcmd and consistancy
PT_NODEKEEPALIVEMIS,
PT_SERVERTICS, // All cmds for the tic.
PT_SERVERREFUSE, // Server refuses joiner (reason inside).
PT_SERVERSHUTDOWN,
PT_CLIENTQUIT, // Client closes the connection.
PT_ASKINFO, // Anyone can ask info of the server.
PT_SERVERINFO, // Send game & server info (gamespy).
PT_PLAYERINFO, // Send information for players in game (gamespy).
PT_REQUESTFILE, // Client requests a file transfer
PT_ASKINFOVIAMS, // Packet from the MS requesting info be sent to new client.
// If this ID changes, update masterserver definition.
PT_RESYNCHEND, // Player is now resynched and is being requested to remake the gametic
PT_RESYNCHGET, // Player got resynch packet
// Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL
// allows HSendPacket(,true,,) to return false.
// In addition, this packet can't occupy all the available slots.
PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file.
PT_TEXTCMD, // Extra text commands from the client.
PT_TEXTCMD2, // Splitscreen text commands.
PT_CLIENTJOIN, // Client wants to join; used in start game.
PT_NODETIMEOUT, // Packet sent to self if the connection times out.
PT_RESYNCHING, // Packet sent to resync players.
// Blocks game advance until synched.
#ifdef NEWPING
PT_PING, // Packet sent to tell clients the other client's latency to server.
#endif
NUMPACKETTYPE
} packettype_t;
#if defined(_MSC_VER)
#pragma pack(1)
......@@ -85,7 +36,6 @@ typedef struct
{
UINT8 client_tic;
UINT8 resendfrom;
INT16 consistancy;
ticcmd_t cmd;
} ATTRPACK clientcmd_pak;
......@@ -95,7 +45,6 @@ typedef struct
{
UINT8 client_tic;
UINT8 resendfrom;
INT16 consistancy;
ticcmd_t cmd, cmd2;
} ATTRPACK client2cmd_pak;
......@@ -113,148 +62,6 @@ typedef struct
ticcmd_t cmds[45]; // normally [BACKUPTIC][MAXPLAYERS] but too large
} ATTRPACK servertics_pak;
// sent to client when all consistency data
// for players has been restored
typedef struct
{
UINT32 randomseed;
//ctf flag stuff
SINT8 flagplayer[2];
INT32 flagloose[2];
INT32 flagflags[2];
fixed_t flagx[2];
fixed_t flagy[2];
fixed_t flagz[2];
UINT32 ingame; // spectator bit for each player
UINT32 ctfteam; // if not spectator, then which team?
// Resynch game scores and the like all at once
UINT32 score[MAXPLAYERS]; // Everyone's score.
INT16 numboxes[MAXPLAYERS];
INT16 totalring[MAXPLAYERS];
tic_t realtime[MAXPLAYERS];
UINT8 laps[MAXPLAYERS];
} ATTRPACK resynchend_pak;
typedef struct
{
//player stuff
UINT8 playernum;
// Do not send anything visual related.
// Only send data that we need to know for physics.
UINT8 playerstate; //playerstate_t
UINT32 pflags; //pflags_t
UINT8 panim; //panim_t
angle_t aiming;
INT32 currentweapon;
INT32 ringweapons;
UINT16 powers[NUMPOWERS];
// Score is resynched in the confirm resync packet
INT32 health;
SINT8 lives;
SINT8 continues;
UINT8 scoreadd;
SINT8 xtralife;
SINT8 pity;
UINT8 skincolor;
INT32 skin;
// Just in case Lua does something like
// modify these at runtime
fixed_t normalspeed;
fixed_t runspeed;
UINT8 thrustfactor;
UINT8 accelstart;
UINT8 acceleration;
UINT8 charability;
UINT8 charability2;
UINT32 charflags;
UINT32 thokitem; //mobjtype_t
UINT32 spinitem; //mobjtype_t
UINT32 revitem; //mobjtype_t
fixed_t actionspd;
fixed_t mindash;
fixed_t maxdash;
fixed_t jumpfactor;
fixed_t speed;
UINT8 jumping;
UINT8 secondjump;
UINT8 fly1;
tic_t glidetime;
UINT8 climbing;
INT32 deadtimer;
tic_t exiting;
UINT8 homing;
tic_t skidtime;
fixed_t cmomx;
fixed_t cmomy;
fixed_t rmomx;
fixed_t rmomy;
INT32 weapondelay;
INT32 tossdelay;
INT16 starpostx;
INT16 starposty;
INT16 starpostz;
INT32 starpostnum;
tic_t starposttime;
angle_t starpostangle;
INT32 maxlink;
fixed_t dashspeed;
INT32 dashtime;
angle_t angle_pos;
angle_t old_angle_pos;
tic_t bumpertime;
INT32 flyangle;
tic_t drilltimer;
INT32 linkcount;
tic_t linktimer;
INT32 anotherflyangle;
tic_t nightstime;
INT32 drillmeter;
UINT8 drilldelay;
UINT8 bonustime;
UINT8 mare;
INT16 lastsidehit, lastlinehit;
tic_t losstime;
UINT8 timeshit;
INT32 onconveyor;
//player->mo stuff
UINT8 hasmo; //boolean
angle_t angle;
fixed_t x;
fixed_t y;
fixed_t z;
fixed_t momx;
fixed_t momy;
fixed_t momz;
fixed_t friction;
fixed_t movefactor;
INT32 tics;
statenum_t statenum;
UINT32 flags;
UINT32 flags2;
UINT16 eflags;
fixed_t radius;
fixed_t height;
fixed_t scale;
fixed_t destscale;
fixed_t scalespeed;
} ATTRPACK resynch_pak;
typedef struct
{
UINT8 version; // different versions don't work
......@@ -383,9 +190,6 @@ typedef struct
client2cmd_pak client2pak; // 200 bytes
servertics_pak serverpak; // 132495 bytes
serverconfig_pak servercfg; // 773 bytes
resynchend_pak resynchend; //
resynch_pak resynchpak; //
UINT8 resynchgot; //
UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes
filetx_pak filetxpak; // 139 bytes
clientconfig_pak clientcfg; // 136 bytes
......@@ -395,9 +199,6 @@ typedef struct
msaskinfo_pak msaskinfo; // 22 bytes
plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes
plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes
#ifdef NEWPING
UINT32 pingtable[MAXPLAYERS]; // 128 bytes
#endif
} u; // this is needed to pack diff packet types data together
} ATTRPACK doomdata_t;
......@@ -406,35 +207,32 @@ typedef struct
#endif
#define MAXSERVERLIST 64 // depends only on the display
typedef struct
{
SINT8 node;
serverinfo_pak info;
} serverelem_t;
extern serverelem_t serverlist[MAXSERVERLIST];
extern UINT32 serverlistcount;
extern INT32 mapchangepending;
// points inside doomcom
extern doomdata_t *netbuffer;
extern consvar_t cv_playbackspeed;
#define BASEPACKETSIZE ((size_t)&(((doomdata_t *)0)->u))
#define FILETXHEADER ((size_t)((filetx_pak *)0)->data)
#define BASESERVERTICSSIZE ((size_t)&(((doomdata_t *)0)->u.serverpak.cmds[0]))
#define KICK_MSG_GO_AWAY 1
#define KICK_MSG_CON_FAIL 2
#define KICK_MSG_PLAYER_QUIT 3
#define KICK_MSG_TIMEOUT 4
#define KICK_MSG_BANNED 5
#ifdef NEWPING
#define KICK_MSG_PING_HIGH 6
#endif
#define KICK_MSG_CUSTOM_KICK 7
#define KICK_MSG_CUSTOM_BAN 8
typedef enum {
// Player left
KICK_MSG_PLAYER_QUIT,
// Generic kick/ban
KICK_MSG_GO_AWAY,
KICK_MSG_BANNED,
// Custom kick/ban
KICK_MSG_CUSTOM_KICK,
KICK_MSG_CUSTOM_BAN,
// Networking errors
KICK_MSG_TIMEOUT,
KICK_MSG_PING_HIGH,
KICK_MSG_XD_FAIL,
KICK_MSG_STOP_HACKING
} kickmsg_e;
extern boolean server;
extern boolean dedicated; // for dedicated server
......@@ -442,15 +240,7 @@ extern UINT16 software_MAXPACKETLENGTH;
extern boolean acceptnewnode;
extern SINT8 servernode;
void Command_Ping_f(void);
extern tic_t connectiontimeout;
#ifdef NEWPING
extern UINT16 pingmeasurecount;
extern UINT32 realpingtable[MAXPLAYERS];
extern UINT32 playerpingtable[MAXPLAYERS];
#endif
extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend;
extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_maxsend;
// used in d_net, the only dependence
tic_t ExpandTics(INT32 low);
......@@ -473,6 +263,7 @@ void CL_AddSplitscreenPlayer(void);
void CL_RemoveSplitscreenPlayer(void);
void CL_Reset(void);
void CL_ClearPlayer(INT32 playernum);
void CL_RemovePlayer(INT32 playernum);
void CL_UpdateServerList(boolean internetsearch, INT32 room);
// is there a game running
boolean Playing(void);
......@@ -504,5 +295,4 @@ void D_ResetTiccmds(void);
tic_t GetLag(INT32 node);
UINT8 GetFreeXCmdSize(void);
extern UINT8 hu_resynching;
#endif
#include "doomdef.h"
#include "doomstat.h"
#include "byteptr.h"
#include "d_datawrap.h"
#include "z_zone.h"
static void CheckEOF(DataWrap dw, size_t l)
{
if (dw->p - dw->data + l > dw->len)
{
Z_Free(dw);
longjmp(*dw->eofjmp, 1);
}
}
UINT8 DW_ReadUINT8(DataWrap dw)
{
CheckEOF(dw, 1);
return READUINT8(dw->p);
}
UINT16 DW_ReadUINT16(DataWrap dw)
{
CheckEOF(dw, 2);
return READUINT16(dw->p);
}
UINT32 DW_ReadUINT32(DataWrap dw)
{
CheckEOF(dw, 4);
return READUINT32(dw->p);
}
SINT8 DW_ReadSINT8(DataWrap dw)
{
CheckEOF(dw, 1);
return READSINT8(dw->p);
}
INT16 DW_ReadINT16(DataWrap dw)
{
CheckEOF(dw, 2);
return READINT16(dw->p);
}
INT32 DW_ReadINT32(DataWrap dw)
{
CheckEOF(dw, 4);
return READINT32(dw->p);
}
fixed_t DW_ReadFixed(DataWrap dw)
{
CheckEOF(dw, 4);
return READFIXED(dw->p);
}
char *DW_ReadStringn(DataWrap dw, size_t n)
{
char *string = ZZ_Alloc(n+1);
char *p = string;
size_t i;
for (i = 0; i < n; i++, p++)
{
CheckEOF(dw,1);
*p = READUINT8(dw->p);
if (!*p)
break;
}
*p = '\0';
return string;
}
DataWrap D_NewDataWrap(const void *data, size_t len, jmp_buf *eofjmp)
{
DataWrap dw = ZZ_Alloc(sizeof(struct DataWrap_s));
dw->data = dw->p = data;
dw->len = len;
dw->eofjmp = eofjmp;
return dw;
}
// Basically SDL_RWops I guess.
#include <setjmp.h>
typedef struct DataWrap_s {
const void *data, *p;
size_t len;
jmp_buf *eofjmp;
} *DataWrap;
UINT8 DW_ReadUINT8(DataWrap);
UINT16 DW_ReadUINT16(DataWrap);
UINT32 DW_ReadUINT32(DataWrap);
SINT8 DW_ReadSINT8(DataWrap);
INT16 DW_ReadINT16(DataWrap);
INT32 DW_ReadINT32(DataWrap);
fixed_t DW_ReadFixed(DataWrap);
char *DW_ReadStringn(DataWrap, size_t n);
DataWrap D_NewDataWrap(const void *data, size_t len, jmp_buf *eofjmp);
#include <enet/enet.h>
#include "doomdef.h"
#include "doomstat.h"
#include "byteptr.h"
#include "d_enet.h"
#include "z_zone.h"
#include "m_menu.h"
#include "d_datawrap.h"
#include "g_game.h"
#include "p_local.h"
#include "d_main.h"
#include "i_system.h"
#include "m_argv.h"
#include "m_random.h"
UINT8 net_nodecount, net_playercount;
UINT16 net_ringid;
UINT8 playernode[MAXPLAYERS];
SINT8 nodetoplayer[MAXNETNODES];
SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen
boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
#define MAX_SERVER_MESSAGE 320
static UINT8 *net_buffer = NULL;
static UINT16 portnum = 5029;
static tic_t lastMove;
static ticcmd_t lastCmd;
static UINT16 *removelist, removecount;
enum {
CHANNEL_GENERAL = 0,
CHANNEL_CHAT,
CHANNEL_MOVE,
NET_CHANNELS,
DISCONNECT_UNKNOWN = 0,
DISCONNECT_SHUTDOWN,
DISCONNECT_FULL,
DISCONNECT_VERSION,
CLIENT_ASKMSINFO = 0,
CLIENT_JOIN,
CLIENT_CHAT,
CLIENT_CHARACTER,
CLIENT_MOVE,
CLIENT_ATTACK,
SERVER_MSINFO = 0,
SERVER_MAPINFO,
SERVER_MESSAGE,
SERVER_SPAWN,
SERVER_KILL,
SERVER_REMOVE,
SERVER_REMOVE_LIST,
SERVER_MOVE,
SERVER_PLAYER_DAMAGE,
SERVER_PLAYER_RINGS,
SERVER_FORCE_MOVE,
SERVER_POWERUP
};
static ENetHost *ServerHost = NULL,
*ClientHost = NULL;
static ENetPeer *nodetopeer[MAXNETNODES];
typedef struct {
ticcmd_t cmd;
fixed_t x,y,z,
vx,vy,vz,
ax,ay,az;
angle_t angle, aiming;
enum state state;
} GhostData;
typedef struct PeerData_s {
UINT8 node;
UINT8 flags;
char name[MAXPLAYERNAME+1];
GhostData ghost;
} PeerData;
enum {
PEER_LEAVING = 1
};
static UINT8 mynode;
void Net_ServerMessage(const char *fmt, ...);
static void ServerSendMapInfo(UINT8 node);
static void Net_MovePlayers(void);
static void Net_SendRemoveList(UINT8 node);
static void Net_ForceMove(player_t *player);
void Net_GetNetStat(UINT8 node, UINT32 *ping, UINT32 *packetLoss)
{
ENetPeer *peer;
I_Assert(node < MAXNETNODES);
I_Assert(nodeingame[node]);
peer = nodetopeer[node];
if (peer == NULL)
return;
if (ping)
*ping = peer->lastRoundTripTime;
if (packetLoss)
*packetLoss = peer->packetLoss;
}
static void DisconnectNode(UINT8 node, UINT8 why)
{
if (nodetopeer[node] == NULL)
return;
PeerData *pdata = nodetopeer[node]->data;
pdata->flags |= PEER_LEAVING;
enet_peer_disconnect(nodetopeer[node], why);
}
static void ServerHandlePacket(UINT8 node, DataWrap data)
{
PeerData *pdata = nodetopeer[node]->data;
switch(DW_ReadUINT8(data))
{
case CLIENT_JOIN:
{
UINT16 version = DW_ReadUINT16(data);
UINT16 subversion = DW_ReadUINT16(data);
if (version != VERSION || subversion != SUBVERSION)
{
CONS_Printf("NETWORK: Version mismatch!?\n");
DisconnectNode(node, DISCONNECT_VERSION);
break;
}
char *name = DW_ReadStringn(data, MAXPLAYERNAME);
strcpy(pdata->name, name);
Z_Free(name);
Net_ServerMessage("%s connected.", pdata->name);
net_playercount++;
ServerSendMapInfo(node);
break;
}
case CLIENT_CHAT:
Net_ServerMessage("\3<%s> %s", pdata->name, DW_ReadStringn(data, 256));
break;
case CLIENT_CHARACTER:
{
UINT8 pnum, i;
// this message might be sent when the player first spawns
if (nodetoplayer[node] == -1)
{
for (pnum = 0; pnum < MAXPLAYERS; pnum++)
if (!playeringame[pnum])
break;
I_Assert(pnum != MAXPLAYERS);
CL_ClearPlayer(pnum);
playeringame[pnum] = true;
G_AddPlayer(pnum);
playernode[pnum] = node;
nodetoplayer[node] = pnum;
// Update them on the current game state.
// Send players
for (i = 0; i < MAXPLAYERS; i++)
if (i != pnum && playeringame[i])
Net_SpawnPlayer(i, node);
// Send which mobjs have already been removed.
Net_SendRemoveList(node);
}
pnum = nodetoplayer[node];
SetPlayerSkin(pnum, DW_ReadStringn(data, SKINNAMESIZE));
players[pnum].skincolor = DW_ReadUINT8(data) % MAXSKINCOLORS;
Net_SpawnPlayer(pnum, 0);
break;
}
case CLIENT_MOVE:
{
player_t *player;
GhostData ghost;
fixed_t oldz;
ghost.cmd.forwardmove = DW_ReadSINT8(data);
ghost.cmd.sidemove = DW_ReadSINT8(data);
ghost.cmd.angleturn = DW_ReadINT16(data);
ghost.cmd.aiming = DW_ReadINT16(data);
ghost.cmd.buttons = DW_ReadUINT16(data);
ghost.x = DW_ReadFixed(data);
ghost.y = DW_ReadFixed(data);
ghost.z = DW_ReadFixed(data);
// TODO: Compare new data to old data, etc.
memcpy(&pdata->ghost, &ghost, sizeof(ghost));
if (nodetoplayer[node] == -1)
break;
player = &players[nodetoplayer[node]];
player->cmd.forwardmove = ghost.cmd.forwardmove;
player->cmd.sidemove = ghost.cmd.sidemove;
player->cmd.angleturn = ghost.cmd.angleturn;
player->cmd.aiming = ghost.cmd.aiming;
player->cmd.buttons = ghost.cmd.buttons;
oldz = player->mo->z;
player->mo->z = ghost.z;
if (!P_TryMove(player->mo, ghost.x, ghost.y, true))
{
player->mo->z = oldz;
Net_ForceMove(player);
}
P_SetTarget(&tmthing, NULL);
break;
}
case CLIENT_ATTACK:
{
player_t *player;
fixed_t x,y,z,oldz;
if (nodetoplayer[node] == -1)
break;
player = &players[nodetoplayer[node]];
if (player->pflags & PF_JUMPED)
break;
x = DW_ReadFixed(data);
y = DW_ReadFixed(data);
z = DW_ReadFixed(data);
oldz = player->mo->z;
player->mo->z = z;
if (!P_TryMove(player->mo, x, y, true))
{
player->mo->z = oldz;
Net_ForceMove(player);
}
player->pflags |= PF_JUMPDOWN;
P_DoJump(player, true);
P_SetTarget(&tmthing, NULL);
break;
}
default:
CONS_Printf("NETWORK: Unknown message type recieved from node %u!\n", node);
break;
}
}
void CL_ConnectionSuccessful(void);
static void ClientHandlePacket(DataWrap data)
{
player_t *player = &players[consoleplayer];
switch(DW_ReadUINT8(data))
{
case SERVER_MAPINFO:
{
CL_ConnectionSuccessful();
mynode = DW_ReadUINT8(data);
CONS_Printf("NETWORK: I'm player %u\n", mynode);
gamemap = DW_ReadINT16(data);
gametype = DW_ReadINT16(data);
G_InitNew(false, G_BuildMapName(gamemap), true, true);
M_StartControlPanel();
M_SetupNetgameChoosePlayer();
break;
}
case SERVER_MESSAGE:
{
char *msg = DW_ReadStringn(data, MAX_SERVER_MESSAGE);
CONS_Printf("%s\n", msg);
Z_Free(msg);
break;
}
case SERVER_SPAWN:
{
UINT16 id = DW_ReadUINT16(data);
if (id-1 == mynode)
break;
// Spawn a player.
if (id < 1000)
{
mobj_t *mobj = P_SpawnMobj(0, 0, 0, MT_PLAYER);
mobj->flags &= ~MF_SOLID;
mobj->flags |= MF_SLIDEME;
mobj->mobjnum = id;
mobj->skin = &skins[DW_ReadUINT8(data)];
mobj->color = DW_ReadUINT8(data);
}
break;
}
case SERVER_KILL:
{
thinker_t *th;
mobj_t *target = NULL;
mobj_t *killer = NULL;
UINT16 id = DW_ReadUINT16(data);
UINT16 kid = DW_ReadUINT16(data);
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
{
mobj_t *mobj = (mobj_t *)th;
if (mobj->mobjnum == id)
target = mobj;
if (kid && mobj->mobjnum == kid)
killer = mobj;
if (target && (killer || !kid))
break;
}
if (target)
{
target->mobjnum = 0;
if (target->flags & MF_SPECIAL && !(target->flags & (MF_ENEMY|MF_BOSS)))
S_StartSound(killer, target->info->deathsound);
P_KillMobj(target, killer, killer, 0);
}
break;
}
case SERVER_REMOVE:
{
thinker_t *th;
mobj_t *mobj = NULL;
UINT16 id = DW_ReadUINT16(data);
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker && ((mobj_t *)th)->mobjnum == id)
{
mobj = (mobj_t *)th;
break;
}
if (mobj)
P_RemoveMobj(mobj);
break;
}
case SERVER_REMOVE_LIST:
{
thinker_t *th;
mobj_t *mobj = NULL;
UINT16 i;
removecount = DW_ReadUINT16(data);
removelist = ZZ_Alloc(removecount * sizeof(UINT16));
for (i = 0; i < removecount; i++)
removelist[i] = DW_ReadUINT16(data);
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker && ((mobj_t *)th)->mobjnum != 0)
{
mobj = (mobj_t *)th;
for (i = 0; i < removecount; i++)
if (mobj->mobjnum == removelist[i])
{
P_RemoveMobj(mobj);
break;
}
}
break;
}
case SERVER_MOVE:
{
thinker_t *th;
mobj_t *mobj = NULL;
UINT16 id = DW_ReadUINT16(data);
fixed_t x,y,z;
if (id-1 == mynode)
break;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker && ((mobj_t *)th)->mobjnum == id)
{
mobj = (mobj_t *)th;
break;
}
if (!mobj)
{
//CONS_Printf("NETWORK: Failed to find mobj with id %u\n", id);
break;
}
x = DW_ReadINT16(data) << 16,
y = DW_ReadINT16(data) << 16,
z = DW_ReadINT16(data) << 16;
P_UnsetThingPosition(mobj);
mobj->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY;
P_SetThingPosition(mobj);
P_TeleportMove(mobj, x, y, z);
P_SetTarget(&tmthing, NULL);
mobj->momx = DW_ReadINT16(data) << 8,
mobj->momy = DW_ReadINT16(data) << 8,
mobj->momz = DW_ReadINT16(data) << 8;
mobj->angle = DW_ReadUINT8(data) << 24;
if (mobj->type == MT_PLAYER)
{
mobj->sprite = SPR_PLAY;
mobj->sprite2 = DW_ReadUINT8(data);
mobj->frame = DW_ReadUINT8(data);
mobj->tics = -1;
}
break;
}
case SERVER_PLAYER_DAMAGE:
{
const UINT8 damagetype = DW_ReadUINT8(data);
const fixed_t x = DW_ReadFixed(data),
y = DW_ReadFixed(data),
z = DW_ReadFixed(data);
player->mo->z = z;
P_TryMove(player->mo, x, y, true);
P_SetTarget(&tmthing, NULL);
if (!(damagetype & DMG_DEATHMASK) && (player->health > 1 || player->powers[pw_shield]))
{
P_DoPlayerPain(player, NULL, NULL);
if (player->powers[pw_shield])
P_RemoveShield(player);
else
P_PlayerRingBurst(player, player->health - 1);
if (damagetype == DMG_SPIKE)
S_StartSound(player->mo, sfx_spkdth);
else if (player->powers[pw_shield])
S_StartSound(player->mo, sfx_shldls);
else
P_PlayRinglossSound(player->mo);
}
else
P_KillMobj(player->mo, NULL, NULL, damagetype);
break;
}
case SERVER_PLAYER_RINGS:
player->health = DW_ReadUINT16(data) + 1;
player->mo->health = player->health;
break;
case SERVER_FORCE_MOVE:
{
const fixed_t x = DW_ReadFixed(data),
y = DW_ReadFixed(data),
z = DW_ReadFixed(data);
P_TeleportMove(player->mo, x, y, z);
P_SetTarget(&tmthing, NULL);
break;
}
case SERVER_POWERUP:
{
powertype_t power = DW_ReadUINT8(data);
UINT16 setting = DW_ReadUINT16(data);
switch(power)
{
case pw_shield:
player->powers[pw_shield] = setting | (player->powers[pw_shield] & SH_STACK);
switch(setting)
{
case SH_FORCE:
player->powers[pw_shield] |= 1;
break;
case SH_ELEMENTAL:
if (player->powers[pw_underwater] > 1 && player->powers[pw_underwater] <= 12*TICRATE + 1)
P_RestoreMusic(player);
player->powers[pw_underwater] = 0;
if (player->powers[pw_spacetime] > 1)
P_RestoreMusic(player);
player->powers[pw_spacetime] = 0;
break;
default:
break;
}
S_StartSound(player->mo, sfx_shield);
P_SpawnShieldOrb(player);
break;
default:
CONS_Printf("NETWORK: Unknown power type recieved from server.\n");
break;
}
break;
}
default:
CONS_Printf("NETWORK: Unknown message type recieved from server!\n");
break;
}
}
void Net_AckTicker(void)
{
ENetEvent e;
UINT8 i;
PeerData *pdata;
jmp_buf safety;
Net_MovePlayers();
while (ClientHost && enet_host_service(ClientHost, &e, 0) > 0)
switch (e.type)
{
case ENET_EVENT_TYPE_DISCONNECT:
if (!server)
{
INT32 disconnect_type = e.data;
nodetopeer[servernode] = NULL;
enet_host_destroy(ClientHost);
ClientHost = NULL;
CL_Reset();
D_StartTitle();
switch(disconnect_type)
{
case DISCONNECT_SHUTDOWN:
M_StartMessage(M_GetText("Server shutting down.\n\nPress ESC\n"), NULL, MM_NOTHING);
break;
case DISCONNECT_FULL:
M_StartMessage(M_GetText("Server is full.\n\nPress ESC\n"), NULL, MM_NOTHING);
break;
case DISCONNECT_VERSION:
M_StartMessage(M_GetText("Game version mismatch.\n\nPress ESC\n"), NULL, MM_NOTHING);
break;
default:
M_StartMessage(M_GetText("Disconnected from server.\n\nPress ESC\n"), NULL, MM_NOTHING);
break;
}
}
break;
case ENET_EVENT_TYPE_RECEIVE:
if (setjmp(safety))
CONS_Printf("NETWORK: There was an EOF error in a recieved packet from server! len %u\n", e.packet->dataLength);
else
ClientHandlePacket(D_NewDataWrap(e.packet->data, e.packet->dataLength, &safety));
enet_packet_destroy(e.packet);
break;
default:
break;
}
while (ServerHost && enet_host_service(ServerHost, &e, 0) > 0)
switch (e.type)
{
case ENET_EVENT_TYPE_CONNECT:
// turn away new connections when maxplayers is hit
// TODO: wait to see if it's a remote console first or something.
if (net_playercount >= cv_maxplayers.value)
{
CONS_Printf("NETWORK: New connection tossed, server is full.\n");
enet_peer_disconnect(e.peer, DISCONNECT_FULL);
break;
}
for (i = 0; i < MAXNETNODES && nodeingame[i]; i++)
;
I_Assert(i < MAXNETNODES); // ENet should not be able to send connect events when nodes are full.
net_nodecount++;
nodeingame[i] = true;
nodetopeer[i] = e.peer;
pdata = ZZ_Alloc(sizeof(*pdata));
pdata->node = i;
pdata->flags = 0;
strcpy(pdata->name, "New Player");
memset(&pdata->ghost, 0, sizeof(GhostData));
e.peer->data = pdata;
CONS_Printf("NETWORK: Node %u connected.\n", i);
break;
case ENET_EVENT_TYPE_DISCONNECT:
if (!e.peer->data)
break;
pdata = (PeerData *)e.peer->data;
if (!(pdata->flags & PEER_LEAVING))
Net_ServerMessage("%s disconnected.", pdata->name);
if (playeringame[nodetoplayer[pdata->node]])
{
Net_SendRemove(pdata->node);
CL_RemovePlayer(nodetoplayer[pdata->node]);
}
net_nodecount--;
nodetopeer[pdata->node] = NULL;
nodeingame[pdata->node] = false;
nodetoplayer[pdata->node] = -1;
Z_Free(pdata);
e.peer->data = NULL;
break;
case ENET_EVENT_TYPE_RECEIVE:
if (!e.peer->data)
break;
pdata = (PeerData *)e.peer->data;
if (!(pdata->flags & PEER_LEAVING))
{
if (setjmp(safety))
CONS_Printf("NETWORK: There was an EOF error in a recieved packet! Node %u, len %u\n", pdata->node, e.packet->dataLength);
else
ServerHandlePacket(pdata->node, D_NewDataWrap(e.packet->data, e.packet->dataLength, &safety));
}
enet_packet_destroy(e.packet);
break;
default:
break;
}
}
void D_NetOpen(void)
{
ENetAddress address = { ENET_HOST_ANY, portnum };
ServerHost = enet_host_create(&address, MAXNETNODES, NET_CHANNELS, 0, 0);
if (!ServerHost)
I_Error("ENet failed to open server host. (Check if the port is in use?)");
if (!net_buffer)
net_buffer = ZZ_Alloc(4096);
if (!net_buffer)
I_Error("Failed to allocate net_buffer");
servernode = 0;
nodeingame[servernode] = true;
net_nodecount = 1;
net_playercount = 0;
lastMove = I_GetTime();
}
boolean D_NetConnect(const char *hostname, const char *port)
{
ENetAddress address;
ENetEvent e;
ClientHost = enet_host_create(NULL, 1, NET_CHANNELS, 0, 0);
if (!ClientHost)
I_Error("ENet failed to initialize client host.\n");
if (!net_buffer)
net_buffer = ZZ_Alloc(4096);
netgame = multiplayer = true;
servernode = 1;
lastMove = I_GetTime();
memset(&lastCmd, 0, sizeof(ticcmd_t));
enet_address_set_host(&address, hostname);
address.port = 5029;
if (port != NULL)
address.port = atoi(port) || address.port;
nodetopeer[servernode] = enet_host_connect(ClientHost, &address, NET_CHANNELS, 0);
if (!nodetopeer[servernode])
I_Error("Failed to allocate ENet peer for connecting ???\n");
nodeingame[servernode] = true;
if (enet_host_service(ClientHost, &e, 5000) > 0
&& e.type == ENET_EVENT_TYPE_CONNECT)
{
CONS_Printf("NETWORK: Connection successful!\n");
return true;
}
CONS_Printf("NETWORK: Connection failed...\n");
server = true;
servernode = 0;
enet_host_destroy(ClientHost);
ClientHost = NULL;
return false;
}
// Initialize network.
// netgame is set to true before this is called if -server was passed.
void D_CheckNetGame(void)
{
if (enet_initialize())
I_Error("Failed to initialize ENet.\n");
if ((M_CheckParm("-port") || M_CheckParm("-udpport")) && M_IsNextParm())
{
portnum = (UINT16)atoi(M_GetNextParm());
CONS_Printf("Port number changed to %u\n", portnum);
}
D_ClientServerInit();
}
void D_CloseConnection(void)
{
ENetEvent e;
if (ServerHost)
{
UINT8 i, waiting=0;
// tell everyone to go away
for (i = 0; i < MAXNETNODES; i++)
if (nodeingame[i] && servernode != i)
{
enet_peer_disconnect(nodetopeer[i], DISCONNECT_SHUTDOWN);
waiting++;
}
// wait for messages to go through.
while (waiting > 0 && enet_host_service(ServerHost, &e, 3000) > 0)
switch (e.type)
{
// i don't care, shut up.
case ENET_EVENT_TYPE_RECEIVE:
enet_packet_destroy(e.packet);
break;
// good, go away.
case ENET_EVENT_TYPE_DISCONNECT:
if (e.peer->data)
{
PeerData *pdata = (PeerData *)e.peer->data;
if (playeringame[nodetoplayer[pdata->node]])
CL_RemovePlayer(nodetoplayer[pdata->node]);
nodetopeer[pdata->node] = NULL;
nodeingame[pdata->node] = false;
nodetoplayer[pdata->node] = -1;
Z_Free(pdata);
e.peer->data = NULL;
}
waiting--;
break;
// no, we're shutting down.
case ENET_EVENT_TYPE_CONNECT:
enet_peer_reset(e.peer);
break;
default:
break;
}
// clean up the remaining nodes
for (i = 0; i < MAXNETNODES; i++)
if (nodetopeer[i])
{
enet_peer_reset(nodetopeer[i]);
nodetopeer[i] = NULL;
nodeingame[i] = false;
nodetoplayer[i] = -1;
}
// alright, we're finished.
enet_host_destroy(ServerHost);
ServerHost = NULL;
}
if (ClientHost)
{
enet_peer_disconnect(nodetopeer[servernode], DISCONNECT_SHUTDOWN);
while (enet_host_service(ClientHost, &e, 3000) > 0)
{
if (e.type == ENET_EVENT_TYPE_DISCONNECT)
break;
else switch (e.type)
{
case ENET_EVENT_TYPE_RECEIVE:
enet_packet_destroy(e.packet);
break;
case ENET_EVENT_TYPE_CONNECT:
// how the what ???
enet_peer_reset(e.peer);
break;
default:
break;
}
}
nodeingame[servernode] = false;
nodetopeer[servernode] = NULL;
servernode = 0;
server = true;
enet_host_destroy(ClientHost);
ClientHost = NULL;
}
netgame = false;
addedtogame = false;
net_nodecount = net_playercount = 0;
}
void Net_CloseConnection(INT32 node)
{
DisconnectNode(node, 0);
}
// Client: Can I play? =3 My name is Player so-and-so!
void Net_SendJoin(void)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
if (!netgame)
return;
WRITEUINT8(buf, CLIENT_JOIN);
WRITEUINT16(buf, VERSION);
WRITEUINT16(buf, SUBVERSION);
WRITESTRINGN(buf, cv_playername.string, MAXPLAYERNAME);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(nodetopeer[servernode], CHANNEL_GENERAL, packet);
}
static void ServerSendMapInfo(UINT8 node)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
if (!netgame)
return;
WRITEUINT8(buf, SERVER_MAPINFO);
WRITEUINT8(buf, node);
WRITEINT16(buf, gamemap);
WRITEINT16(buf, gametype);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(nodetopeer[node], CHANNEL_GENERAL, packet);
}
void Net_ServerMessage(const char *fmt, ...)
{
va_list argptr;
UINT8 *buf = net_buffer;
if (!netgame)
return;
WRITEUINT8(buf, SERVER_MESSAGE);
va_start(argptr, fmt);
vsnprintf((char *)buf, MAX_SERVER_MESSAGE, fmt, argptr);
va_end(argptr);
buf += strlen((char *)buf)+1;
CONS_Printf("%s\n", net_buffer+1);
enet_host_broadcast(ServerHost, 0, enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE));
}
void Net_SendChat(char *line)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
if (!netgame)
return;
if (server)
{
WRITEUINT8(buf, SERVER_MESSAGE);
sprintf((char *)buf, "\3<~%s> %s", cv_playername.string, line);
buf += strlen((char *)buf)+1;
CONS_Printf("%s\n", net_buffer+1);
}
else
{
WRITEUINT8(buf, CLIENT_CHAT);
WRITESTRINGN(buf, line, 256);
}
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
if (server)
enet_host_broadcast(ServerHost, CHANNEL_CHAT, packet);
else
enet_peer_send(nodetopeer[servernode], CHANNEL_CHAT, packet);
}
void Net_SendCharacter(void)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
if (!netgame || server)
return;
WRITEUINT8(buf, CLIENT_CHARACTER);
WRITESTRINGN(buf, cv_skin.string, SKINNAMESIZE);
WRITEUINT8(buf, cv_playercolor.value);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(nodetopeer[servernode], CHANNEL_GENERAL, packet);
}
void Net_SendClientMove(boolean force)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
boolean reliable = false;
player_t *player = &players[consoleplayer];
if (!netgame || server || !addedtogame || !player->mo)
return;
// only update once a second unless buttons changed.
if (force || memcmp(&lastCmd, &players[consoleplayer].cmd, sizeof(ticcmd_t)))
reliable = true;
if (lastMove+NEWTICRATE < I_GetTime() && !reliable)
return;
lastMove = I_GetTime();
G_CopyTiccmd(&lastCmd, &player->cmd, 1);
WRITEUINT8(buf, CLIENT_MOVE);
WRITESINT8(buf, player->cmd.forwardmove);
WRITESINT8(buf, player->cmd.sidemove);
WRITEINT16(buf, player->cmd.angleturn);
WRITEINT16(buf, player->cmd.aiming);
WRITEUINT16(buf, player->cmd.buttons);
WRITEFIXED(buf, player->mo->x);
WRITEFIXED(buf, player->mo->y);
WRITEFIXED(buf, player->mo->z);
packet = enet_packet_create(net_buffer, buf-net_buffer, reliable ? ENET_PACKET_FLAG_RELIABLE : 0);
enet_peer_send(nodetopeer[servernode], CHANNEL_MOVE, packet);
}
void Net_SendClientJump(void)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
if (!netgame || server || !addedtogame || !players[consoleplayer].mo)
return;
WRITEUINT8(buf, CLIENT_ATTACK);
WRITEFIXED(buf, players[consoleplayer].mo->x);
WRITEFIXED(buf, players[consoleplayer].mo->y);
WRITEFIXED(buf, players[consoleplayer].mo->z);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(nodetopeer[servernode], CHANNEL_MOVE, packet);
}
void Net_SpawnPlayer(UINT8 pnum, UINT8 node)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
if (!netgame)
return;
WRITEUINT8(buf, SERVER_SPAWN);
WRITEUINT16(buf, playernode[pnum]+1);
/*WRITEINT16(buf, players[pnum].mo->x >> 16);
WRITEINT16(buf, players[pnum].mo->y >> 16);
WRITEINT16(buf, players[pnum].mo->z >> 16);
WRITEUINT8(buf, players[pnum].mo->angle >> 24);*/
WRITEUINT8(buf, players[pnum].skin);
WRITEUINT8(buf, players[pnum].skincolor);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
if (node == 0)
enet_host_broadcast(ServerHost, CHANNEL_MOVE, packet);
else
enet_peer_send(nodetopeer[node], CHANNEL_MOVE, packet);
}
static void Net_MovePlayers(void)
{
ENetPacket *packet;
UINT8 *buf, i;
if (!netgame || !server || lastMove+NEWTICRATE < I_GetTime())
return;
lastMove = I_GetTime();
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || !players[i].mo)
continue;
buf = net_buffer;
WRITEUINT8(buf, SERVER_MOVE);
WRITEUINT16(buf, playernode[i]+1);
WRITEINT16(buf, players[i].mo->x >> 16);
WRITEINT16(buf, players[i].mo->y >> 16);
WRITEINT16(buf, players[i].mo->z >> 16);
WRITEINT16(buf, players[i].mo->momx >> 8);
WRITEINT16(buf, players[i].mo->momy >> 8);
WRITEINT16(buf, players[i].mo->momz >> 8);
WRITEUINT8(buf, players[i].mo->angle >> 24);
if (players[i].mo->sprite == SPR_PLAY)
WRITEUINT8(buf, players[i].mo->sprite2);
else
WRITEUINT8(buf, SPR2_STND);
WRITEUINT8(buf, players[i].mo->frame);
packet = enet_packet_create(net_buffer, buf-net_buffer, 0);
enet_host_broadcast(ServerHost, CHANNEL_MOVE, packet);
}
}
void Net_SendPlayerDamage(UINT8 pnum, UINT8 damagetype)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
if (!netgame || !server || !nodetopeer[playernode[pnum]])
return;
WRITEUINT8(buf, SERVER_PLAYER_DAMAGE);
WRITEUINT8(buf, damagetype);
WRITEFIXED(buf, players[pnum].mo->x);
WRITEFIXED(buf, players[pnum].mo->y);
WRITEFIXED(buf, players[pnum].mo->z);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(nodetopeer[playernode[pnum]], CHANNEL_GENERAL, packet);
}
void Net_SendMobjMove(mobj_t *mobj)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
if (!netgame || !server || mobj->mobjnum == 0)
return;
WRITEUINT8(buf, SERVER_MOVE);
WRITEUINT16(buf, mobj->mobjnum);
WRITEINT16(buf, mobj->x >> 16);
WRITEINT16(buf, mobj->y >> 16);
WRITEINT16(buf, mobj->z >> 16);
WRITEINT16(buf, mobj->momx >> 8);
WRITEINT16(buf, mobj->momy >> 8);
WRITEINT16(buf, mobj->momz >> 8);
WRITEUINT8(buf, mobj->angle >> 24);
packet = enet_packet_create(net_buffer, buf-net_buffer, 0);
enet_host_broadcast(ServerHost, CHANNEL_MOVE, packet);
}
void Net_SendRemove(UINT16 id)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
UINT16 i;
if (!netgame || !server || id == 0)
return;
if (id >= 1000 && id < net_ringid)
{
for (i = 0; i < removecount; i++)
if (removelist[i] == id)
break;
if (i == removecount)
{
removelist = Z_Realloc(removelist, ++removecount * sizeof(UINT16), PU_LEVEL, NULL);
removelist[removecount-1] = id;
}
}
WRITEUINT8(buf, SERVER_REMOVE);
WRITEUINT16(buf, id);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
enet_host_broadcast(ServerHost, CHANNEL_MOVE, packet);
}
void Net_SendKill(UINT16 id, UINT16 kid)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
UINT16 i;
if (!netgame || !server || id == 0)
return;
if (id >= 1000 && id < net_ringid)
{
for (i = 0; i < removecount; i++)
if (removelist[i] == id)
break;
if (i == removecount)
{
removelist = Z_Realloc(removelist, ++removecount * sizeof(UINT16), PU_LEVEL, NULL);
removelist[removecount-1] = id;
}
}
WRITEUINT8(buf, SERVER_KILL);
WRITEUINT16(buf, id);
WRITEUINT16(buf, kid);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
enet_host_broadcast(ServerHost, CHANNEL_MOVE, packet);
}
void Net_SendPlayerRings(UINT8 pnum)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
if (!netgame || !server || !nodetopeer[playernode[pnum]])
return;
WRITEUINT8(buf, SERVER_PLAYER_RINGS);
WRITEUINT16(buf, players[pnum].mo->health-1);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(nodetopeer[playernode[pnum]], CHANNEL_GENERAL, packet);
}
void Net_ResetLevel(void)
{
removelist = NULL;
removecount = 0;
}
static void Net_SendRemoveList(UINT8 node)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
UINT16 i;
if (!netgame || !server || removecount == 0)
return;
WRITEUINT8(buf, SERVER_REMOVE_LIST);
WRITEUINT16(buf, removecount);
for (i = 0; i < removecount; i++)
WRITEUINT16(buf, removelist[i]);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(nodetopeer[node], CHANNEL_GENERAL, packet);
}
static void Net_ForceMove(player_t *player)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
if (!netgame || !server)
return;
WRITEUINT8(buf, SERVER_FORCE_MOVE);
WRITEFIXED(buf, player->mo->x);
WRITEFIXED(buf, player->mo->y);
WRITEFIXED(buf, player->mo->z);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(nodetopeer[playernode[player-players]], CHANNEL_GENERAL, packet);
}
void Net_AwardPowerup(player_t *player, powertype_t power, UINT16 data)
{
ENetPacket *packet;
UINT8 *buf = net_buffer;
if (!netgame || !server)
return;
WRITEUINT8(buf, SERVER_POWERUP);
WRITEUINT8(buf, power);
WRITEUINT16(buf, data);
packet = enet_packet_create(net_buffer, buf-net_buffer, ENET_PACKET_FLAG_RELIABLE);
enet_peer_send(nodetopeer[playernode[player-players]], CHANNEL_GENERAL, packet);
}
// Make sure we allocate a network node for every player, "server full" denials, the Master server heartbeat, and potential RCON connections.
#define MAXNETNODES MAXPLAYERS+2
extern UINT8 net_nodecount, net_playercount;
extern UINT16 net_ringid;
extern UINT8 playernode[MAXPLAYERS];
extern SINT8 nodetoplayer[MAXNETNODES];
extern SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
extern UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen
extern boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
void D_NetOpen(void);
boolean D_NetConnect(const char *hostname, const char *port);
void Net_GetNetStat(UINT8 node, UINT32 *ping, UINT32 *packetLoss);
void Net_AckTicker(void);
void D_CheckNetGame(void);
void D_CloseConnection(void);
void Net_CloseConnection(INT32 node);
void Net_SendJoin(void);
void Net_SendCharacter(void);
void Net_SendClientMove(boolean force);
void Net_SendClientJump(void);
void Net_SpawnPlayer(UINT8 pnum, UINT8 node);
void Net_SendChat(char *line);
void Net_SendPlayerDamage(UINT8 pnum, UINT8 damagetype);
void Net_SendMobjMove(mobj_t *mobj);
void Net_SendRemove(UINT16 id);
void Net_SendKill(UINT16 id, UINT16 kid);
void Net_SendPlayerRings(UINT8 pnum);
void Net_ResetLevel(void);
void Net_AwardPowerup(player_t *player, powertype_t power, UINT16 data);
......@@ -44,7 +44,6 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#include "doomdef.h"
#include "am_map.h"
#include "console.h"
#include "d_net.h"
#include "f_finale.h"
#include "g_game.h"
#include "hu_stuff.h"
......@@ -68,11 +67,11 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#include "m_cheat.h"
#include "y_inter.h"
#include "p_local.h" // chasecam
#include "mserv.h" // ms_RoomId
#include "m_misc.h" // screenshot functionality
#include "dehacked.h" // Dehacked list test
#include "m_cond.h" // condition initialization
#include "fastcmp.h"
#include "d_enet.h"
#ifdef CMAKECONFIG
#include "config.h"
......@@ -100,6 +99,9 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#include "lua_script.h"
#endif
FILE *debugfile;
// platform independant focus loss
UINT8 window_notinfocus = false;
......@@ -465,21 +467,17 @@ static void D_Display(void)
//
if (!wipe)
{
if (cv_netstat.value)
if (cv_netstat.value && netgame && !server)
{
char s[50];
Net_GetNetStat();
s[sizeof s - 1] = '\0';
snprintf(s, sizeof s - 1, "get %d b/s", getbps);
V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-40, V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "send %d b/s", sendbps);
V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-30, V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "GameMiss %.2f%%", gamelostpercent);
V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-20, V_YELLOWMAP, s);
snprintf(s, sizeof s - 1, "SysMiss %.2f%%", lostpercent);
V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-10, V_YELLOWMAP, s);
char stat[50];
UINT32 ping = 0, loss = 0;
stat[sizeof stat - 1] = '\0';
Net_GetNetStat(servernode, &ping, &loss);
snprintf(stat, sizeof stat - 1, "ping: %u ms (%u frames)", ping, ping / (1000/NEWTICRATE));
V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-20, V_YELLOWMAP|V_SNAPTORIGHT|V_SNAPTOBOTTOM, stat);
snprintf(stat, sizeof stat - 1, "loss: %u", loss);
V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-10, V_YELLOWMAP|V_SNAPTORIGHT|V_SNAPTOBOTTOM, stat);
}
I_FinishUpdate(); // page flip or blit buffer
......@@ -566,11 +564,6 @@ void D_SRB2Loop(void)
HW3S_BeginFrameUpdate();
#endif
// don't skip more than 10 frames at a time
// (fadein / fadeout cause massive frame skip!)
if (realtics > 8)
realtics = 1;
// process tics (but maybe not if realtic == 0)
TryRunTics(realtics);
......@@ -589,14 +582,6 @@ void D_SRB2Loop(void)
}
else if (rendertimeout < entertic) // in case the server hang or netsplit
{
// Lagless camera! Yay!
if (gamestate == GS_LEVEL && netgame)
{
if (splitscreen && camera2.chase)
P_MoveChaseCamera(&players[secondarydisplayplayer], &camera2, false);
if (camera.chase)
P_MoveChaseCamera(&players[displayplayer], &camera, false);
}
D_Display();
if (moviemode)
......@@ -1206,21 +1191,9 @@ void D_SRB2Main(void)
CONS_Printf("ST_Init(): Init status bar.\n");
ST_Init();
if (M_CheckParm("-room"))
{
if (!M_IsNextParm())
I_Error("usage: -room <room_id>\nCheck the Master Server's webpage for room ID numbers.\n");
ms_RoomId = atoi(M_GetNextParm());
#ifdef UPDATE_ALERT
GetMODVersion_Console();
#endif
}
// init all NETWORK
CONS_Printf("D_CheckNetGame(): Checking network game status.\n");
if (D_CheckNetGame())
autostart = true;
D_CheckNetGame();
// check for a driver that wants intermission stats
// start the apropriate game based on parms
......
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2016 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file d_net.c
/// \brief SRB2 network game communication and protocol, all OS independent parts.
//
/// Implement a Sliding window protocol without receiver window
/// (out of order reception)
/// This protocol uses a mix of "goback n" and "selective repeat" implementation
/// The NOTHING packet is sent when connection is idle to acknowledge packets
#include "doomdef.h"
#include "g_game.h"
#include "i_net.h"
#include "i_system.h"
#include "m_argv.h"
#include "d_net.h"
#include "w_wad.h"
#include "d_netfil.h"
#include "d_clisrv.h"
#include "z_zone.h"
#include "i_tcp.h"
//
// NETWORKING
//
// gametic is the tic about to be (or currently being) run
// server:
// maketic is the tic that hasn't had control made for it yet
// nettics: is the tic for each node
// firsttictosend: is the lowest value of nettics
// client:
// neededtic: is the tic needed by the client to run the game
// firsttictosend: is used to optimize a condition
// normally maketic >= gametic > 0
#define FORCECLOSE 0x8000
tic_t connectiontimeout = (15*TICRATE);
/// \brief network packet
doomcom_t *doomcom = NULL;
/// \brief network packet data, points inside doomcom
doomdata_t *netbuffer = NULL;
FILE *debugfile = NULL; // put some net info in a file during the game
#define MAXREBOUND 8
static doomdata_t reboundstore[MAXREBOUND];
static INT16 reboundsize[MAXREBOUND];
static INT32 rebound_head, rebound_tail;
/// \brief bandwith of netgame
INT32 net_bandwidth;
/// \brief max length per packet
INT16 hardware_MAXPACKETLENGTH;
void (*I_NetGet)(void) = NULL;
void (*I_NetSend)(void) = NULL;
boolean (*I_NetCanSend)(void) = NULL;
boolean (*I_NetCanGet)(void) = NULL;
void (*I_NetCloseSocket)(void) = NULL;
void (*I_NetFreeNodenum)(INT32 nodenum) = NULL;
SINT8 (*I_NetMakeNodewPort)(const char *address, const char* port) = NULL;
boolean (*I_NetOpenSocket)(void) = NULL;
boolean (*I_Ban) (INT32 node) = NULL;
void (*I_ClearBans)(void) = NULL;
const char *(*I_GetNodeAddress) (INT32 node) = NULL;
const char *(*I_GetBanAddress) (size_t ban) = NULL;
const char *(*I_GetBanMask) (size_t ban) = NULL;
boolean (*I_SetBanAddress) (const char *address, const char *mask) = NULL;
boolean *bannednode = NULL;
// network stats
static tic_t statstarttic;
INT32 getbytes = 0;
INT64 sendbytes = 0;
static INT32 retransmit = 0, duppacket = 0;
static INT32 sendackpacket = 0, getackpacket = 0;
INT32 ticruned = 0, ticmiss = 0;
// globals
INT32 getbps, sendbps;
float lostpercent, duppercent, gamelostpercent;
INT32 packetheaderlength;
boolean Net_GetNetStat(void)
{
const tic_t t = I_GetTime();
static INT64 oldsendbyte = 0;
if (statstarttic+STATLENGTH <= t)
{
const tic_t df = t-statstarttic;
const INT64 newsendbyte = sendbytes - oldsendbyte;
sendbps = (INT32)(newsendbyte*TICRATE)/df;
getbps = (getbytes*TICRATE)/df;
if (sendackpacket)
lostpercent = 100.0f*(float)retransmit/(float)sendackpacket;
else
lostpercent = 0.0f;
if (getackpacket)
duppercent = 100.0f*(float)duppacket/(float)getackpacket;
else
duppercent = 0.0f;
if (ticruned)
gamelostpercent = 100.0f*(float)ticmiss/(float)ticruned;
else
gamelostpercent = 0.0f;
ticmiss = ticruned = 0;
oldsendbyte = sendbytes;
getbytes = 0;
sendackpacket = getackpacket = duppacket = retransmit = 0;
statstarttic = t;
return 1;
}
return 0;
}
// -----------------------------------------------------------------
// Some structs and functions for acknowledgement of packets
// -----------------------------------------------------------------
#define MAXACKPACKETS 96 // minimum number of nodes
#define MAXACKTOSEND 96
#define URGENTFREESLOTENUM 10
#define ACKTOSENDTIMEOUT (TICRATE/11)
#ifndef NONET
typedef struct
{
UINT8 acknum;
UINT8 nextacknum;
UINT8 destinationnode;
tic_t senttime;
UINT16 length;
UINT16 resentnum;
union {
SINT8 raw[MAXPACKETLENGTH];
doomdata_t data;
} pak;
} ackpak_t;
#endif
typedef enum
{
CLOSE = 1, // flag is set when connection is closing
} node_flags_t;
#ifndef NONET
// table of packet that was not acknowleged can be resend (the sender window)
static ackpak_t ackpak[MAXACKPACKETS];
#endif
typedef struct
{
// ack return to send (like sliding window protocol)
UINT8 firstacktosend;
// when no consecutive packets are received we keep in mind what packets
// we already received in a queue
UINT8 acktosend_head;
UINT8 acktosend_tail;
UINT8 acktosend[MAXACKTOSEND];
// automatically send keep alive packet when not enough trafic
tic_t lasttimeacktosend_sent;
// detect connection lost
tic_t lasttimepacketreceived;
// flow control: do not send too many packets with ack
UINT8 remotefirstack;
UINT8 nextacknum;
UINT8 flags;
#ifndef NEWPING
// jacobson tcp timeout evaluation algorithm (Karn variation)
fixed_t ping;
fixed_t varping;
INT32 timeout; // computed with ping and varping
#endif
} node_t;
static node_t nodes[MAXNETNODES];
#ifndef NEWPING
#define PINGDEFAULT ((200*TICRATE*FRACUNIT)/1000)
#define VARPINGDEFAULT ((50*TICRATE*FRACUNIT)/1000)
#define TIMEOUT(p,v) (p+4*v+FRACUNIT/2)>>FRACBITS;
#else
#define NODETIMEOUT 14 //What the above boiled down to...
#endif
#ifndef NONET
// return <0 if a < b (mod 256)
// 0 if a = n (mod 256)
// >0 if a > b (mod 256)
// mnemonic: to use it compare to 0: cmpack(a,b)<0 is "a < b" ...
FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b)
{
register INT32 d = a - b;
if (d >= 127 || d < -128)
return -d;
return d;
}
// return a free acknum and copy netbuffer in the ackpak table
static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
{
node_t *node = &nodes[doomcom->remotenode];
INT32 i, numfreeslote = 0;
if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0)
{
DEBFILE(va("too fast %d %d\n",node->remotefirstack,node->nextacknum));
return false;
}
for (i = 0; i < MAXACKPACKETS; i++)
if (!ackpak[i].acknum)
{
// for low priority packet, make sure let freeslotes so urgents packets can be sent
numfreeslote++;
if (netbuffer->packettype >= PT_CANFAIL && numfreeslote < URGENTFREESLOTENUM)
continue;
ackpak[i].acknum = node->nextacknum;
ackpak[i].nextacknum = node->nextacknum;
node->nextacknum++;
if (!node->nextacknum)
node->nextacknum++;
ackpak[i].destinationnode = (UINT8)(node - nodes);
ackpak[i].length = doomcom->datalength;
if (lowtimer)
{
// lowtime mean can't be sent now so try it soon as possible
ackpak[i].senttime = 0;
ackpak[i].resentnum = 1;
}
else
{
ackpak[i].senttime = I_GetTime();
ackpak[i].resentnum = 0;
}
M_Memcpy(ackpak[i].pak.raw, netbuffer, ackpak[i].length);
*freeack = ackpak[i].acknum;
sendackpacket++; // for stat
return true;
}
#ifdef PARANOIA
CONS_Debug(DBG_NETPLAY, "No more free ackpacket\n");
#endif
if (netbuffer->packettype < PT_CANFAIL)
I_Error("Connection lost\n");
return false;
}
// Get a ack to send in the queu of this node
static UINT8 GetAcktosend(INT32 node)
{
nodes[node].lasttimeacktosend_sent = I_GetTime();
return nodes[node].firstacktosend;
}
static void Removeack(INT32 i)
{
INT32 node = ackpak[i].destinationnode;
#ifndef NEWPING
fixed_t trueping = (I_GetTime() - ackpak[i].senttime)<<FRACBITS;
if (ackpak[i].resentnum)
{
// +FRACUNIT/2 for round
nodes[node].ping = (nodes[node].ping*7 + trueping)/8;
nodes[node].varping = (nodes[node].varping*7 + abs(nodes[node].ping-trueping))/8;
nodes[node].timeout = TIMEOUT(nodes[node].ping,nodes[node].varping);
}
DEBFILE(va("Remove ack %d trueping %d ping %f var %f timeout %d\n",ackpak[i].acknum,trueping>>FRACBITS,(double)FIXED_TO_FLOAT(nodes[node].ping),(double)FIXED_TO_FLOAT(nodes[node].varping),nodes[node].timeout));
#else
DEBFILE(va("Remove ack %d\n",ackpak[i].acknum));
#endif
ackpak[i].acknum = 0;
if (nodes[node].flags & CLOSE)
Net_CloseConnection(node);
}
// we have got a packet proceed the ack request and ack return
static boolean Processackpak(void)
{
INT32 i;
boolean goodpacket = true;
node_t *node = &nodes[doomcom->remotenode];
// received an ack return, so remove the ack in the list
if (netbuffer->ackreturn && cmpack(node->remotefirstack, netbuffer->ackreturn) < 0)
{
node->remotefirstack = netbuffer->ackreturn;
// search the ackbuffer and free it
for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes
&& cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0)
{
Removeack(i);
}
}
// received a packet with ack, queue it to send the ack back
if (netbuffer->ack)
{
UINT8 ack = netbuffer->ack;
getackpacket++;
if (cmpack(ack, node->firstacktosend) <= 0)
{
DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack));
duppacket++;
goodpacket = false; // discard packet (duplicate)
}
else
{
// check if it is not already in the queue
for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND)
if (node->acktosend[i] == ack)
{
DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack));
duppacket++;
goodpacket = false; // discard packet (duplicate)
break;
}
if (goodpacket)
{
// is a good packet so increment the acknowledge number,
// then search for a "hole" in the queue
UINT8 nextfirstack = (UINT8)(node->firstacktosend + 1);
if (!nextfirstack)
nextfirstack = 1;
if (ack == nextfirstack)
{
UINT8 hm1; // head - 1
boolean change = true;
node->firstacktosend = nextfirstack++;
if (!nextfirstack)
nextfirstack = 1;
hm1 = (UINT8)((node->acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND);
while (change)
{
change = false;
for (i = node->acktosend_tail; i != node->acktosend_head;
i = (i+1) % MAXACKTOSEND)
{
if (cmpack(node->acktosend[i], nextfirstack) <= 0)
{
if (node->acktosend[i] == nextfirstack)
{
node->firstacktosend = nextfirstack++;
if (!nextfirstack)
nextfirstack = 1;
change = true;
}
if (i == node->acktosend_tail)
{
node->acktosend[node->acktosend_tail] = 0;
node->acktosend_tail = (UINT8)((i+1) % MAXACKTOSEND);
}
else if (i == hm1)
{
node->acktosend[hm1] = 0;
node->acktosend_head = hm1;
hm1 = (UINT8)((hm1-1+MAXACKTOSEND) % MAXACKTOSEND);
}
}
}
}
}
else // out of order packet
{
// don't increment firsacktosend, put it in asktosend queue
// will be incremented when the nextfirstack comes (code above)
UINT8 newhead = (UINT8)((node->acktosend_head+1) % MAXACKTOSEND);
DEBFILE(va("out of order packet (%d expected)\n", nextfirstack));
if (newhead != node->acktosend_tail)
{
node->acktosend[node->acktosend_head] = ack;
node->acktosend_head = newhead;
}
else // buffer full discard packet, sender will resend it
{ // we can admit the packet but we will not detect the duplication after :(
DEBFILE("no more freeackret\n");
goodpacket = false;
}
}
}
}
}
return goodpacket;
}
#endif
// send special packet with only ack on it
void Net_SendAcks(INT32 node)
{
#ifdef NONET
(void)node;
#else
netbuffer->packettype = PT_NOTHING;
M_Memcpy(netbuffer->u.textcmd, nodes[node].acktosend, MAXACKTOSEND);
HSendPacket(node, false, 0, MAXACKTOSEND);
#endif
}
#ifndef NONET
static void GotAcks(void)
{
INT32 i, j;
for (j = 0; j < MAXACKTOSEND; j++)
if (netbuffer->u.textcmd[j])
for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode)
{
if (ackpak[i].acknum == netbuffer->u.textcmd[j])
Removeack(i);
else
// nextacknum is first equal to acknum, then when receiving bigger ack
// there is big chance the packet is lost
// when resent, nextacknum = nodes[node].nextacknum
// will redo the same but with different value
if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0
&& ackpak[i].senttime > 0)
{
ackpak[i].senttime--; // hurry up
}
}
}
#endif
static inline void Net_ConnectionTimeout(INT32 node)
{
// send a very special packet to self (hack the reboundstore queue)
// main code will handle it
reboundstore[rebound_head].packettype = PT_NODETIMEOUT;
reboundstore[rebound_head].ack = 0;
reboundstore[rebound_head].ackreturn = 0;
reboundstore[rebound_head].u.textcmd[0] = (UINT8)node;
reboundsize[rebound_head] = (INT16)(BASEPACKETSIZE + 1);
rebound_head = (rebound_head+1) % MAXREBOUND;
// do not redo it quickly (if we do not close connection it is
// for a good reason!)
nodes[node].lasttimepacketreceived = I_GetTime();
}
// resend the data if needed
void Net_AckTicker(void)
{
#ifndef NONET
INT32 i;
for (i = 0; i < MAXACKPACKETS; i++)
{
const INT32 nodei = ackpak[i].destinationnode;
node_t *node = &nodes[nodei];
#ifdef NEWPING
if (ackpak[i].acknum && ackpak[i].senttime + NODETIMEOUT < I_GetTime())
#else
if (ackpak[i].acknum && ackpak[i].senttime + node->timeout < I_GetTime())
#endif
{
if (ackpak[i].resentnum > 10 && (node->flags & CLOSE))
{
DEBFILE(va("ack %d sent 10 times so connection is supposed lost: node %d\n",
i, nodei));
Net_CloseConnection(nodei | FORCECLOSE);
ackpak[i].acknum = 0;
continue;
}
#ifdef NEWPING
DEBFILE(va("Resend ack %d, %u<%d at %u\n", ackpak[i].acknum, ackpak[i].senttime,
NODETIMEOUT, I_GetTime()));
#else
DEBFILE(va("Resend ack %d, %u<%d at %u\n", ackpak[i].acknum, ackpak[i].senttime,
node->timeout, I_GetTime()));
#endif
M_Memcpy(netbuffer, ackpak[i].pak.raw, ackpak[i].length);
ackpak[i].senttime = I_GetTime();
ackpak[i].resentnum++;
ackpak[i].nextacknum = node->nextacknum;
retransmit++; // for stat
HSendPacket((INT32)(node - nodes), false, ackpak[i].acknum,
(size_t)(ackpak[i].length - BASEPACKETSIZE));
}
}
for (i = 1; i < MAXNETNODES; i++)
{
// this is something like node open flag
if (nodes[i].firstacktosend)
{
// we haven't sent a packet for a long time
// acknowledge packet if needed
if (nodes[i].lasttimeacktosend_sent + ACKTOSENDTIMEOUT < I_GetTime())
Net_SendAcks(i);
if (!(nodes[i].flags & CLOSE)
&& nodes[i].lasttimepacketreceived + connectiontimeout < I_GetTime())
{
Net_ConnectionTimeout(i);
}
}
}
#endif
}
// remove last packet received ack before resending the ackret
// (the higher layer doesn't have room, or something else ....)
void Net_UnAcknowledgPacket(INT32 node)
{
#ifdef NONET
(void)node;
#else
INT32 hm1 = (nodes[node].acktosend_head-1+MAXACKTOSEND) % MAXACKTOSEND;
DEBFILE(va("UnAcknowledge node %d\n", node));
if (!node)
return;
if (nodes[node].acktosend[hm1] == netbuffer->ack)
{
nodes[node].acktosend[hm1] = 0;
nodes[node].acktosend_head = (UINT8)hm1;
}
else if (nodes[node].firstacktosend == netbuffer->ack)
{
nodes[node].firstacktosend--;
if (!nodes[node].firstacktosend)
nodes[node].firstacktosend = UINT8_MAX;
}
else
{
while (nodes[node].firstacktosend != netbuffer->ack)
{
nodes[node].acktosend_tail = (UINT8)
((nodes[node].acktosend_tail-1+MAXACKTOSEND) % MAXACKTOSEND);
nodes[node].acktosend[nodes[node].acktosend_tail] = nodes[node].firstacktosend;
nodes[node].firstacktosend--;
if (!nodes[node].firstacktosend)
nodes[node].firstacktosend = UINT8_MAX;
}
nodes[node].firstacktosend++;
if (!nodes[node].firstacktosend)
nodes[node].firstacktosend = 1;
}
#endif
}
boolean Net_AllAckReceived(void)
{
#ifndef NONET
INT32 i;
for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum)
return false;
#endif
return true;
}
// wait for all ackreturns with timeout in seconds
void Net_WaitAllAckReceived(UINT32 timeout)
{
#ifdef NONET
(void)timeout;
#else
tic_t tictac = I_GetTime();
timeout = tictac + timeout*NEWTICRATE;
HGetPacket();
while (timeout > I_GetTime() && !Net_AllAckReceived())
{
while (tictac == I_GetTime())
I_Sleep();
tictac = I_GetTime();
HGetPacket();
Net_AckTicker();
}
#endif
}
static void InitNode(INT32 node)
{
nodes[node].acktosend_head = nodes[node].acktosend_tail = 0;
#ifndef NEWPING
nodes[node].ping = PINGDEFAULT;
nodes[node].varping = VARPINGDEFAULT;
nodes[node].timeout = TIMEOUT(nodes[node].ping,nodes[node].varping);
#endif
nodes[node].firstacktosend = 0;
nodes[node].nextacknum = 1;
nodes[node].remotefirstack = 0;
nodes[node].flags = 0;
}
static void InitAck(void)
{
INT32 i;
#ifndef NONET
for (i = 0; i < MAXACKPACKETS; i++)
ackpak[i].acknum = 0;
#endif
for (i = 0; i < MAXNETNODES; i++)
InitNode(i);
}
void Net_AbortPacketType(UINT8 packettype)
{
#ifdef NONET
(void)packettype;
#else
INT32 i;
for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum && (ackpak[i].pak.data.packettype == packettype
|| packettype == UINT8_MAX))
{
ackpak[i].acknum = 0;
}
#endif
}
// -----------------------------------------------------------------
// end of acknowledge function
// -----------------------------------------------------------------
// remove a node, clear all ack from this node and reset askret
void Net_CloseConnection(INT32 node)
{
#ifdef NONET
(void)node;
#else
INT32 i;
boolean forceclose = (node & FORCECLOSE) != 0;
node &= ~FORCECLOSE;
if (!node)
return;
nodes[node].flags |= CLOSE;
// try to Send ack back (two army problem)
if (GetAcktosend(node))
{
Net_SendAcks(node);
Net_SendAcks(node);
}
// check if we are waiting for an ack from this node
for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum && ackpak[i].destinationnode == node)
{
if (!forceclose)
return; // connection will be closed when ack is returned
else
ackpak[i].acknum = 0;
}
InitNode(node);
AbortSendFiles(node);
I_NetFreeNodenum(node);
#endif
}
#ifndef NONET
//
// Checksum
//
static UINT32 NetbufferChecksum(void)
{
UINT32 c = 0x1234567;
const INT32 l = doomcom->datalength - 4;
const UINT8 *buf = (UINT8 *)netbuffer + 4;
INT32 i;
for (i = 0; i < l; i++, buf++)
c += (*buf) * (i+1);
return LONG(c);
}
#endif
#ifdef DEBUGFILE
static void fprintfstring(char *s, size_t len)
{
INT32 mode = 0;
size_t i;
for (i = 0; i < len; i++)
if (s[i] < 32)
{
if (!mode)
{
fprintf(debugfile, "[%d", (UINT8)s[i]);
mode = 1;
}
else
fprintf(debugfile, ",%d", (UINT8)s[i]);
}
else
{
if (mode)
{
fprintf(debugfile, "]");
mode = 0;
}
fprintf(debugfile, "%c", s[i]);
}
if (mode)
fprintf(debugfile, "]");
fprintf(debugfile, "\n");
}
static const char *packettypename[NUMPACKETTYPE] =
{
"NOTHING",
"SERVERCFG",
"CLIENTCMD",
"CLIENTMIS",
"CLIENT2CMD",
"CLIENT2MIS",
"NODEKEEPALIVE",
"NODEKEEPALIVEMIS",
"SERVERTICS",
"SERVERREFUSE",
"SERVERSHUTDOWN",
"CLIENTQUIT",
"ASKINFO",
"SERVERINFO",
"REQUESTFILE",
"ASKINFOVIAMS",
"PLAYERCONFIGS",
"FILEFRAGMENT",
"TEXTCMD",
"TEXTCMD2",
"CLIENTJOIN",
"NODETIMEOUT",
};
static void DebugPrintpacket(const char *header)
{
fprintf(debugfile, "%-12s (node %d,ack %d,ackret %d,size %d) type(%d) : %s\n",
header, doomcom->remotenode, netbuffer->ack, netbuffer->ackreturn, doomcom->datalength,
netbuffer->packettype, packettypename[netbuffer->packettype]);
switch (netbuffer->packettype)
{
case PT_ASKINFO:
case PT_ASKINFOVIAMS:
fprintf(debugfile, " time %u\n", (tic_t)LONG(netbuffer->u.askinfo.time) );
break;
case PT_CLIENTJOIN:
fprintf(debugfile, " number %d mode %d\n", netbuffer->u.clientcfg.localplayers,
netbuffer->u.clientcfg.mode);
break;
case PT_SERVERTICS:
fprintf(debugfile, " firsttic %u ply %d tics %d ntxtcmd %s\n ",
(UINT32)ExpandTics(netbuffer->u.serverpak.starttic), netbuffer->u.serverpak.numslots,
netbuffer->u.serverpak.numtics,
sizeu1((size_t)(&((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics])));
fprintfstring((char *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics],(size_t)(
&((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics]));
break;
case PT_CLIENTCMD:
case PT_CLIENT2CMD:
case PT_CLIENTMIS:
case PT_CLIENT2MIS:
case PT_NODEKEEPALIVE:
case PT_NODEKEEPALIVEMIS:
fprintf(debugfile, " tic %4u resendfrom %u\n",
(UINT32)ExpandTics(netbuffer->u.clientpak.client_tic),
(UINT32)ExpandTics (netbuffer->u.clientpak.resendfrom));
break;
case PT_TEXTCMD:
case PT_TEXTCMD2:
fprintf(debugfile, " length %d\n ", netbuffer->u.textcmd[0]);
fprintfstring((char *)netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
break;
case PT_SERVERCFG:
fprintf(debugfile, " playerslots %d clientnode %d serverplayer %d "
"gametic %u gamestate %d gametype %d modifiedgame %d\n",
netbuffer->u.servercfg.totalslotnum, netbuffer->u.servercfg.clientnode,
netbuffer->u.servercfg.serverplayer, (UINT32)LONG(netbuffer->u.servercfg.gametic),
netbuffer->u.servercfg.gamestate, netbuffer->u.servercfg.gametype,
netbuffer->u.servercfg.modifiedgame);
break;
case PT_SERVERINFO:
fprintf(debugfile, " '%s' player %d/%d, map %s, filenum %d, time %u \n",
netbuffer->u.serverinfo.servername, netbuffer->u.serverinfo.numberofplayer,
netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.mapname,
netbuffer->u.serverinfo.fileneedednum,
(UINT32)LONG(netbuffer->u.serverinfo.time));
fprintfstring((char *)netbuffer->u.serverinfo.fileneeded,
(UINT8)((UINT8 *)netbuffer + doomcom->datalength
- (UINT8 *)netbuffer->u.serverinfo.fileneeded));
break;
case PT_SERVERREFUSE:
fprintf(debugfile, " reason %s\n", netbuffer->u.serverrefuse.reason);
break;
case PT_FILEFRAGMENT:
fprintf(debugfile, " fileid %d datasize %d position %u\n",
netbuffer->u.filetxpak.fileid, (UINT16)SHORT(netbuffer->u.filetxpak.size),
(UINT32)LONG(netbuffer->u.filetxpak.position));
break;
case PT_REQUESTFILE:
default: // write as a raw packet
fprintfstring((char *)netbuffer->u.textcmd,
(UINT8)((UINT8 *)netbuffer + doomcom->datalength - (UINT8 *)netbuffer->u.textcmd));
break;
}
}
#endif
//
// HSendPacket
//
boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength)
{
doomcom->datalength = (INT16)(packetlength + BASEPACKETSIZE);
if (node == 0) // packet is to go back to us
{
if ((rebound_head+1) % MAXREBOUND == rebound_tail)
{
#ifdef PARANOIA
CONS_Debug(DBG_NETPLAY, "No more rebound buf\n");
#endif
return false;
}
M_Memcpy(&reboundstore[rebound_head], netbuffer,
doomcom->datalength);
reboundsize[rebound_head] = doomcom->datalength;
rebound_head = (rebound_head+1) % MAXREBOUND;
#ifdef DEBUGFILE
if (debugfile)
{
doomcom->remotenode = (INT16)node;
DebugPrintpacket("SENDLOCAL");
}
#endif
return true;
}
if (!netgame)
I_Error("Tried to transmit to another node");
#ifdef NONET
(void)node;
(void)reliable;
(void)acknum;
#else
// do this before GetFreeAcknum because this function backup
// the current packet
doomcom->remotenode = (INT16)node;
if (doomcom->datalength <= 0)
{
DEBFILE("HSendPacket: nothing to send\n");
#ifdef DEBUGFILE
if (debugfile)
DebugPrintpacket("TRISEND");
#endif
return false;
}
if (node < MAXNETNODES) // can be a broadcast
netbuffer->ackreturn = GetAcktosend(node);
else
netbuffer->ackreturn = 0;
if (reliable)
{
if (I_NetCanSend && !I_NetCanSend())
{
if (netbuffer->packettype < PT_CANFAIL)
GetFreeAcknum(&netbuffer->ack, true);
DEBFILE("HSendPacket: Out of bandwidth\n");
return false;
}
else if (!GetFreeAcknum(&netbuffer->ack, false))
return false;
}
else
netbuffer->ack = acknum;
netbuffer->checksum = NetbufferChecksum();
sendbytes += packetheaderlength + doomcom->datalength; // for stat
// simulate internet :)
if (true || rand()<(INT32)RAND_MAX/5)
{
#ifdef DEBUGFILE
if (debugfile)
DebugPrintpacket("SEND");
#endif
I_NetSend();
}
#ifdef DEBUGFILE
else if (debugfile)
DebugPrintpacket("NOTSEND");
#endif
#endif // ndef NONET
return true;
}
//
// HGetPacket
// Returns false if no packet is waiting
// Check Datalength and checksum
//
boolean HGetPacket(void)
{
// get a packet from self
if (rebound_tail != rebound_head)
{
M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]);
doomcom->datalength = reboundsize[rebound_tail];
if (netbuffer->packettype == PT_NODETIMEOUT)
doomcom->remotenode = netbuffer->u.textcmd[0];
else
doomcom->remotenode = 0;
rebound_tail = (rebound_tail+1) % MAXREBOUND;
#ifdef DEBUGFILE
if (debugfile)
DebugPrintpacket("GETLOCAL");
#endif
return true;
}
if (!netgame)
return false;
#ifndef NONET
while(true)
{
I_NetGet();
if (doomcom->remotenode == -1)
return false;
getbytes += packetheaderlength + doomcom->datalength; // for stat
if (doomcom->remotenode >= MAXNETNODES)
{
DEBFILE(va("receive packet from node %d !\n", doomcom->remotenode));
continue;
}
nodes[doomcom->remotenode].lasttimepacketreceived = I_GetTime();
if (netbuffer->checksum != NetbufferChecksum())
{
DEBFILE("Bad packet checksum\n");
Net_CloseConnection(doomcom->remotenode);
continue;
}
#ifdef DEBUGFILE
if (debugfile)
DebugPrintpacket("GET");
#endif
// proceed the ack and ackreturn field
if (!Processackpak())
continue; // discarded (duplicated)
// a packet with just ackreturn
if (netbuffer->packettype == PT_NOTHING)
{
GotAcks();
continue;
}
break;
}
#endif // ndef NONET
return true;
}
static void Internal_Get(void)
{
doomcom->remotenode = -1;
}
FUNCNORETURN static ATTRNORETURN void Internal_Send(void)
{
I_Error("Send without netgame\n");
}
static void Internal_FreeNodenum(INT32 nodenum)
{
(void)nodenum;
}
SINT8 I_NetMakeNode(const char *hostname)
{
SINT8 newnode = -1;
if (I_NetMakeNodewPort)
{
char *localhostname = strdup(hostname);
char *t = localhostname;
const char *port;
if (!localhostname)
return newnode;
// retrieve portnum from address!
strtok(localhostname, ":");
port = strtok(NULL, ":");
// remove the port in the hostname as we've it already
while ((*t != ':') && (*t != '\0'))
t++;
*t = '\0';
newnode = I_NetMakeNodewPort(localhostname, port);
free(localhostname);
}
return newnode;
}
void D_SetDoomcom(void)
{
if (doomcom) return;
doomcom = Z_Calloc(sizeof (doomcom_t), PU_STATIC, NULL);
doomcom->id = DOOMCOM_ID;
doomcom->numslots = doomcom->numnodes = 1;
doomcom->gametype = 0;
doomcom->consoleplayer = 0;
doomcom->extratics = 0;
}
//
// D_CheckNetGame
// Works out player numbers among the net participants
//
boolean D_CheckNetGame(void)
{
boolean ret = false;
InitAck();
rebound_tail = rebound_head = 0;
statstarttic = I_GetTime();
I_NetGet = Internal_Get;
I_NetSend = Internal_Send;
I_NetCanSend = NULL;
I_NetCloseSocket = NULL;
I_NetFreeNodenum = Internal_FreeNodenum;
I_NetMakeNodewPort = NULL;
hardware_MAXPACKETLENGTH = MAXPACKETLENGTH;
net_bandwidth = 30000;
// I_InitNetwork sets doomcom and netgame
// check and initialize the network driver
multiplayer = false;
// only dos version with external driver will return true
netgame = I_InitNetwork();
if (!netgame && !I_NetOpenSocket)
{
D_SetDoomcom();
netgame = I_InitTcpNetwork();
}
if (netgame)
ret = true;
if (!server && netgame)
netgame = false;
server = true; // WTF? server always true???
// no! The deault mode is server. Client is set elsewhere
// when the client executes connect command.
doomcom->ticdup = 1;
if (M_CheckParm("-extratic"))
{
if (M_IsNextParm())
doomcom->extratics = (INT16)atoi(M_GetNextParm());
else
doomcom->extratics = 1;
CONS_Printf(M_GetText("Set extratics to %d\n"), doomcom->extratics);
}
if (M_CheckParm("-bandwidth"))
{
if (M_IsNextParm())
{
net_bandwidth = atoi(M_GetNextParm());
if (net_bandwidth < 1000)
net_bandwidth = 1000;
if (net_bandwidth > 100000)
hardware_MAXPACKETLENGTH = MAXPACKETLENGTH;
CONS_Printf(M_GetText("Network bandwidth set to %d\n"), net_bandwidth);
}
else
I_Error("usage: -bandwidth <byte_per_sec>");
}
software_MAXPACKETLENGTH = hardware_MAXPACKETLENGTH;
if (M_CheckParm("-packetsize"))
{
if (M_IsNextParm())
{
INT32 p = atoi(M_GetNextParm());
if (p < 75)
p = 75;
if (p > hardware_MAXPACKETLENGTH)
p = hardware_MAXPACKETLENGTH;
software_MAXPACKETLENGTH = (UINT16)p;
}
else
I_Error("usage: -packetsize <bytes_per_packet>");
}
if (netgame)
multiplayer = true;
if (doomcom->id != DOOMCOM_ID)
I_Error("Doomcom buffer invalid!");
if (doomcom->numnodes > MAXNETNODES)
I_Error("Too many nodes (%d), max:%d", doomcom->numnodes, MAXNETNODES);
netbuffer = (doomdata_t *)(void *)&doomcom->data;
#ifdef DEBUGFILE
#ifdef _arch_dreamcast
//debugfile = stderr;
if (debugfile)
CONS_Printf(M_GetText("debug output to: %s\n"), "STDERR");
#else
if (M_CheckParm("-debugfile"))
{
char filename[20];
INT32 k = doomcom->consoleplayer - 1;
if (M_IsNextParm())
k = atoi(M_GetNextParm()) - 1;
while (!debugfile && k < MAXPLAYERS)
{
k++;
sprintf(filename, "debug%d.txt", k);
debugfile = fopen(filename, "w");
}
if (debugfile)
CONS_Printf(M_GetText("debug output to: %s\n"), filename);
else
CONS_Alert(CONS_WARNING, M_GetText("cannot debug output to file %s!\n"), filename);
}
#endif
#endif
D_ClientServerInit();
return ret;
}
void Command_Ping_f(void)
{
#ifndef NEWPING
if(server)
{
#endif
INT32 i;
for (i = 0; i < MAXPLAYERS;i++)
{
#ifndef NEWPING
const INT32 node = playernode[i];
if (playeringame[i] && node != 0)
CONS_Printf(M_GetText("%.2d : %s\n %d tics, %d ms.\n"), i, player_names[i],
GetLag(node), G_TicsToMilliseconds(GetLag(node)));
#else
if (playeringame[i] && i != 0)
CONS_Printf(M_GetText("%.2d : %s\n %d ms\n"), i, player_names[i], playerpingtable[i]);
#endif
}
#ifndef NEWPING
}
else
CONS_Printf(M_GetText("Only the server can use this.\n"));
#endif
}
void D_CloseConnection(void)
{
INT32 i;
if (netgame)
{
// wait the ackreturn with timout of 5 Sec
Net_WaitAllAckReceived(5);
// close all connection
for (i = 0; i < MAXNETNODES; i++)
Net_CloseConnection(i|FORCECLOSE);
InitAck();
if (I_NetCloseSocket)
I_NetCloseSocket();
I_NetGet = Internal_Get;
I_NetSend = Internal_Send;
I_NetCanSend = NULL;
I_NetCloseSocket = NULL;
I_NetFreeNodenum = Internal_FreeNodenum;
I_NetMakeNodewPort = NULL;
netgame = false;
addedtogame = false;
}
}
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2016 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file d_net.h
/// \brief part of layer 4 (transport) (tp4) of the osi model
/// assure the reception of packet and proceed a checksums
///
/// There is a data struct that stores network communication related
/// stuff, and one that defines the actual packets to be transmitted
#ifndef __D_NET__
#define __D_NET__
// Max computers in a game.
#define MAXNETNODES 32
#define BROADCASTADDR MAXNETNODES
#define MAXSPLITSCREENPLAYERS 2 // max number of players on a single computer
#define STATLENGTH (TICRATE*2)
// stat of net
extern INT32 ticruned, ticmiss;
extern INT32 getbps, sendbps;
extern float lostpercent, duppercent, gamelostpercent;
extern INT32 packetheaderlength;
boolean Net_GetNetStat(void);
extern INT32 getbytes;
extern INT64 sendbytes; // realtime updated
extern SINT8 nodetoplayer[MAXNETNODES];
extern SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
extern UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen
extern boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
void Net_AckTicker(void);
boolean Net_AllAckReceived(void);
// if reliable return true if packet sent, 0 else
boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum,
size_t packetlength);
boolean HGetPacket(void);
void D_SetDoomcom(void);
#ifndef NONET
void D_SaveBan(void);
#endif
boolean D_CheckNetGame(void);
void D_CloseConnection(void);
void Net_UnAcknowledgPacket(INT32 node);
void Net_CloseConnection(INT32 node);
void Net_AbortPacketType(UINT8 packettype);
void Net_SendAcks(INT32 node);
void Net_WaitAllAckReceived(UINT32 timeout);
#endif
......@@ -37,7 +37,6 @@
#include "d_main.h"
#include "m_random.h"
#include "f_finale.h"
#include "mserv.h"
#include "md5.h"
#include "z_zone.h"
#include "lua_script.h"
......@@ -216,12 +215,6 @@ consvar_t cv_respawntime = {"respawndelay", "3", CV_NETVAR|CV_CHEAT, respawntime
consvar_t cv_competitionboxes = {"competitionboxes", "Random", CV_NETVAR|CV_CHEAT, competitionboxes_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#ifdef SEENAMES
static CV_PossibleValue_t seenames_cons_t[] = {{0, "Off"}, {1, "Colorless"}, {2, "Team"}, {3, "Ally/Foe"}, {0, NULL}};
consvar_t cv_seenames = {"seenames", "Ally/Foe", CV_SAVE, seenames_cons_t, 0, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_allowseenames = {"allowseenames", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
// these are just meant to be saved to the config
consvar_t cv_playername = {"name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_playername2 = {"name2", "Tails", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name2_OnChange, 0, NULL, NULL, 0, 0, NULL};
......@@ -338,12 +331,9 @@ consvar_t cv_allowexitlevel = {"allowexitlevel", "No", CV_NETVAR, CV_YesNo, NULL
consvar_t cv_killingdead = {"killingdead", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_netstat = {"netstat", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; // show bandwidth statistics
consvar_t cv_netstat = {"netstat", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; // show bandwidth statistics
static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
consvar_t cv_nettimeout = {"nettimeout", "525", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL};
#ifdef NEWPING
consvar_t cv_maxping = {"maxping", "0", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
// Intermission time Tails 04-19-2002
static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}};
consvar_t cv_inttime = {"inttime", "20", CV_NETVAR, inttime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
......@@ -358,7 +348,7 @@ consvar_t cv_runscripts = {"runscripts", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL
consvar_t cv_pause = {"pausepermission", "Server", CV_NETVAR, pause_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mute = {"mute", "Off", CV_NETVAR|CV_CALL, CV_OnOff, Mute_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_sleep = {"cpusleep", "-1", CV_SAVE, sleeping_cons_t, NULL, -1, NULL, NULL, 0, 0, NULL};
consvar_t cv_sleep = {"cpusleep", "0", CV_NOSHOWHELP, sleeping_cons_t, NULL, -1, NULL, NULL, 0, 0, NULL};
INT16 gametype = GT_COOP;
boolean splitscreen = false;
......@@ -446,9 +436,6 @@ void D_RegisterServerCommands(void)
#endif
#endif
// for master server connection
AddMServCommands();
// p_mobj.c
CV_RegisterVar(&cv_itemrespawntime);
CV_RegisterVar(&cv_itemrespawn);
......@@ -518,18 +505,10 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_maxplayers);
CV_RegisterVar(&cv_maxsend);
COM_AddCommand("ping", Command_Ping_f);
CV_RegisterVar(&cv_nettimeout);
CV_RegisterVar(&cv_skipmapcheck);
CV_RegisterVar(&cv_sleep);
#ifdef NEWPING
CV_RegisterVar(&cv_maxping);
#endif
#ifdef SEENAMES
CV_RegisterVar(&cv_allowseenames);
#endif
CV_RegisterVar(&cv_dummyconsvar);
}
......@@ -612,9 +591,6 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_playercolor2);
CV_RegisterVar(&cv_skin2);
#ifdef SEENAMES
CV_RegisterVar(&cv_seenames);
#endif
CV_RegisterVar(&cv_rollingdemos);
CV_RegisterVar(&cv_netstat);
......@@ -963,7 +939,7 @@ static void SetPlayerName(INT32 playernum, char *newname)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
}
......@@ -1040,116 +1016,9 @@ static INT32 snacpending = 0, snac2pending = 0, chmappending = 0;
//
static void SendNameAndColor(void)
{
XBOXSTATIC char buf[MAXPLAYERNAME+2];
char *p;
p = buf;
// normal player colors
if (G_GametypeHasTeams())
{
if (players[consoleplayer].ctfteam == 1 && cv_playercolor.value != skincolor_redteam)
CV_StealthSetValue(&cv_playercolor, skincolor_redteam);
else if (players[consoleplayer].ctfteam == 2 && cv_playercolor.value != skincolor_blueteam)
CV_StealthSetValue(&cv_playercolor, skincolor_blueteam);
}
// never allow the color "none"
if (!cv_playercolor.value)
{
if (players[consoleplayer].skincolor)
CV_StealthSetValue(&cv_playercolor, players[consoleplayer].skincolor);
else if (skins[players[consoleplayer].skin].prefcolor)
CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin].prefcolor);
else
CV_StealthSet(&cv_playercolor, cv_playercolor.defaultvalue);
}
if (!strcmp(cv_playername.string, player_names[consoleplayer])
&& cv_playercolor.value == players[consoleplayer].skincolor
&& !strcmp(cv_skin.string, skins[players[consoleplayer].skin].name))
return;
// We'll handle it later if we're not playing.
if (!Playing())
return;
// If you're not in a netgame, merely update the skin, color, and name.
if (!netgame)
{
INT32 foundskin;
CleanupPlayerName(consoleplayer, cv_playername.zstring);
strcpy(player_names[consoleplayer], cv_playername.zstring);
players[consoleplayer].skincolor = cv_playercolor.value;
if (players[consoleplayer].mo)
players[consoleplayer].mo->color = players[consoleplayer].skincolor;
if (metalrecording)
{ // Metal Sonic is Sonic, obviously.
SetPlayerSkinByNum(consoleplayer, 0);
CV_StealthSet(&cv_skin, skins[0].name);
}
else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1)
{
boolean notsame;
cv_skin.value = foundskin;
notsame = (cv_skin.value != players[consoleplayer].skin);
SetPlayerSkin(consoleplayer, cv_skin.string);
CV_StealthSet(&cv_skin, skins[cv_skin.value].name);
if (notsame)
{
CV_StealthSetValue(&cv_playercolor, skins[cv_skin.value].prefcolor);
players[consoleplayer].skincolor = (cv_playercolor.value&0x1F) % MAXSKINCOLORS;
if (players[consoleplayer].mo)
players[consoleplayer].mo->color = (UINT8)players[consoleplayer].skincolor;
}
}
else
{
cv_skin.value = players[consoleplayer].skin;
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
// will always be same as current
SetPlayerSkin(consoleplayer, cv_skin.string);
}
return;
}
snacpending++;
// Don't change name if muted
if (cv_mute.value && !(server || adminplayer == consoleplayer))
CV_StealthSet(&cv_playername, player_names[consoleplayer]);
else // Cleanup name if changing it
CleanupPlayerName(consoleplayer, cv_playername.zstring);
// Don't change skin if the server doesn't want you to.
if (!CanChangeSkin(consoleplayer))
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
// check if player has the skin loaded (cv_skin may have
// the name of a skin that was available in the previous game)
cv_skin.value = R_SkinAvailable(cv_skin.string);
if (cv_skin.value < 0)
{
CV_StealthSet(&cv_skin, DEFAULTSKIN);
cv_skin.value = 0;
}
// Finally write out the complete packet and send it off.
WRITESTRINGN(p, cv_playername.zstring, MAXPLAYERNAME);
WRITEUINT8(p, (UINT8)cv_playercolor.value);
WRITEUINT8(p, (UINT8)cv_skin.value);
SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf);
// NET TODO
CV_StealthSetValue(&cv_playercolor, players[consoleplayer].skincolor);
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
}
// splitscreen
......@@ -1308,7 +1177,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
CONS_Alert(CONS_WARNING, M_GetText("Illegal color change received from %s (team: %d), color: %d)\n"), player_names[playernum], p->ctfteam, p->skincolor);
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
return;
}
......@@ -1534,7 +1403,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
mapchangepending = 0;
// spawn the server if needed
// reset players if there is a new one
if (!(adminplayer == consoleplayer) && SV_SpawnServer())
if (server && SV_SpawnServer())
buf[0] &= ~(1<<1);
// Kick bot from special stages
......@@ -1724,7 +1593,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
......@@ -1797,6 +1666,12 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
if (demorecording) // Okay, level loaded, character spawned and skinned,
G_BeginRecording(); // I AM NOW READY TO RECORD.
demo_start = true;
if (netgame && !addedtogame)
{
M_StartControlPanel();
M_SetupNetgameChoosePlayer();
}
}
static void Command_Pause(void)
......@@ -1840,7 +1715,7 @@ static void Got_Pause(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
......@@ -1920,14 +1795,14 @@ static void Got_Suicide(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
}
if (players[suicideplayer].mo)
P_DamageMobj(players[suicideplayer].mo, NULL, NULL, 10000);
P_DamageMobj(players[suicideplayer].mo, NULL, NULL, 1, DMG_INSTAKILL);
}
/** Deals with an ::XD_RANDOMSEED message in a netgame.
......@@ -1989,7 +1864,7 @@ static void Got_Clearscores(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
......@@ -2342,7 +2217,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
}
......@@ -2357,7 +2232,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
......@@ -2396,7 +2271,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
}
......@@ -2449,7 +2324,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
SendNetXCmd(XD_KICK, &buf, 2);
}
......@@ -2458,7 +2333,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
if (players[playernum].mo)
{
if (!players[playernum].spectator)
P_DamageMobj(players[playernum].mo, NULL, NULL, 10000);
P_DamageMobj(players[playernum].mo, NULL, NULL, 1, DMG_INSTAKILL);
else
{
P_RemoveMobj(players[playernum].mo);
......@@ -2746,7 +2621,7 @@ static void Got_Verification(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
......@@ -2826,7 +2701,7 @@ static void Got_MotD_f(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
......@@ -2889,7 +2764,7 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
......@@ -3066,7 +2941,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
CONS_Alert(CONS_WARNING, M_GetText("Illegal addfile command received from %s\n"), player_names[playernum]);
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
return;
}
......@@ -3106,7 +2981,7 @@ static void Got_Delfilecmd(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
......@@ -3139,7 +3014,7 @@ static void Got_Addfilecmd(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
......@@ -3315,7 +3190,7 @@ static void NumLaps_OnChange(void)
static void NetTimeout_OnChange(void)
{
connectiontimeout = (tic_t)cv_nettimeout.value;
// NET TODO
}
UINT32 timelimitintics = 0;
......@@ -3732,14 +3607,15 @@ static void Command_Mapmd5_f(void)
static void Command_ExitLevel_f(void)
{
if (!(netgame || (multiplayer && gametype != GT_COOP)) && !cv_debug)
// NET TODO
/*if (!(netgame || (multiplayer && gametype != GT_COOP)) && !cv_debug)
CONS_Printf(M_GetText("This only works in a netgame.\n"));
else if (!(server || (adminplayer == consoleplayer)))
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
else if (gamestate != GS_LEVEL || demoplayback)
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
else
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
else*/
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
......@@ -3758,7 +3634,7 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
XBOXSTATIC UINT8 buf[2];
buf[0] = (UINT8)playernum;
buf[1] = KICK_MSG_CON_FAIL;
buf[1] = KICK_MSG_STOP_HACKING;
SendNetXCmd(XD_KICK, &buf, 2);
}
return;
......@@ -4059,23 +3935,7 @@ static void Name2_OnChange(void)
*/
static void Skin_OnChange(void)
{
if (!Playing())
return; // do whatever you want
if (!(cv_debug || devparm) && !(multiplayer || netgame) // In single player.
&& (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CONTINUING))
{
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
return;
}
if (CanChangeSkin(consoleplayer) && !P_PlayerMoving(consoleplayer))
SendNameAndColor();
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
}
// NET TODO
}
/** Sends a skin change for the secondary splitscreen player, unless that
......
......@@ -20,9 +20,6 @@
// console vars
extern consvar_t cv_playername;
extern consvar_t cv_playercolor;
#ifdef SEENAMES
extern consvar_t cv_seenames, cv_allowseenames;
#endif
extern consvar_t cv_usemouse;
extern consvar_t cv_usejoystick;
extern consvar_t cv_usejoystick2;
......@@ -115,10 +112,6 @@ extern consvar_t cv_ringslinger, cv_soundtest;
extern consvar_t cv_specialrings, cv_powerstones, cv_matchboxes, cv_competitionboxes;
#ifdef NEWPING
extern consvar_t cv_maxping;
#endif
extern consvar_t cv_skipmapcheck;
extern consvar_t cv_sleep, cv_screenshot_option, cv_screenshot_folder;
......
......@@ -44,12 +44,11 @@
#include "doomdef.h"
#include "doomstat.h"
#include "d_enet.h"
#include "d_main.h"
#include "g_game.h"
#include "i_net.h"
#include "i_system.h"
#include "m_argv.h"
#include "d_net.h"
#include "w_wad.h"
#include "d_netfil.h"
#include "z_zone.h"
......@@ -62,8 +61,6 @@
#include <errno.h>
static void SendFile(INT32 node, const char *filename, UINT8 fileid);
// sender structure
typedef struct filetx_s
{
......@@ -103,45 +100,8 @@ INT32 lastfilenum = 0;
*/
UINT8 *PutFileNeeded(void)
{
size_t i, count = 0;
UINT8 *p = netbuffer->u.serverinfo.fileneeded;
char wadfilename[MAX_WADPATH] = "";
UINT8 filestatus;
size_t bytesused = 0;
for (i = 0; i < numwadfiles; i++)
{
// if it has only music/sound lumps, mark it as unimportant
if (W_VerifyNMUSlumps(wadfiles[i]->filename))
filestatus = 0;
else
filestatus = 1; // important
// Store in the upper four bits
if (!cv_downloading.value)
filestatus += (2 << 4); // won't send
else if ((wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024))
filestatus += (0 << 4); // won't send
else
filestatus += (1 << 4); // will send if requested
bytesused += (nameonlylength(wadfilename) + 22);
// Don't write too far...
if (bytesused > sizeof(netbuffer->u.serverinfo.fileneeded))
I_Error("Too many wad files added to host a game. (%s, stopped on %s)\n", sizeu1(bytesused), wadfilename);
WRITEUINT8(p, filestatus);
count++;
WRITEUINT32(p, wadfiles[i]->filesize);
nameonly(strcpy(wadfilename, wadfiles[i]->filename));
WRITESTRINGN(p, wadfilename, MAX_WADPATH);
WRITEMEM(p, wadfiles[i]->md5sum, 16);
}
netbuffer->u.serverinfo.fileneedednum = (UINT8)count;
return p;
// NET TODO
return NULL;
}
// parse the serverinfo packet and fill fileneeded table on client
......@@ -244,62 +204,8 @@ boolean CL_CheckDownloadable(void)
*/
boolean CL_SendRequestFile(void)
{
char *p;
INT32 i;
INT64 totalfreespaceneeded = 0, availablefreespace;
#ifdef PARANOIA
if (M_CheckParm("-nodownload"))
I_Error("Attempted to download files in -nodownload mode");
for (i = 0; i < fileneedednum; i++)
if (fileneeded[i].status != FS_FOUND && fileneeded[i].status != FS_OPEN
&& fileneeded[i].important && (fileneeded[i].willsend == 0 || fileneeded[i].willsend == 2))
{
I_Error("Attempted to download files that were not sendable");
}
#endif
netbuffer->packettype = PT_REQUESTFILE;
p = (char *)netbuffer->u.textcmd;
for (i = 0; i < fileneedednum; i++)
if ((fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD)
&& fileneeded[i].important)
{
totalfreespaceneeded += fileneeded[i].totalsize;
nameonly(fileneeded[i].filename);
WRITEUINT8(p, i); // fileid
WRITESTRINGN(p, fileneeded[i].filename, MAX_WADPATH);
// put it in download dir
strcatbf(fileneeded[i].filename, downloaddir, "/");
fileneeded[i].status = FS_REQUESTED;
}
WRITEUINT8(p, 0xFF);
I_GetDiskFreeSpace(&availablefreespace);
if (totalfreespaceneeded > availablefreespace)
I_Error("To play on this server you must download %s KB,\n"
"but you have only %s KB free space on this drive\n",
sizeu1((size_t)(totalfreespaceneeded>>10)), sizeu2((size_t)(availablefreespace>>10)));
// prepare to download
I_mkdir(downloaddir, 0755);
return HSendPacket(servernode, true, 0, p - (char *)netbuffer->u.textcmd);
}
// get request filepak and put it on the send queue
void Got_RequestFilePak(INT32 node)
{
char wad[MAX_WADPATH+1];
UINT8 *p = netbuffer->u.textcmd;
UINT8 id;
while (p < netbuffer->u.textcmd + MAXTEXTCMD-1) // Don't allow hacked client to overflow
{
id = READUINT8(p);
if (id == 0xFF)
break;
READSTRINGN(p, wad, MAX_WADPATH);
SendFile(node, wad, id);
}
// NET TODO
return true;
}
// client check if the fileneeded aren't already loaded or on the disk
......@@ -431,74 +337,6 @@ void CL_LoadServerFiles(void)
// little optimization to test if there is a file in the queue
static INT32 filetosend = 0;
static void SendFile(INT32 node, const char *filename, UINT8 fileid)
{
filetx_t **q;
filetx_t *p;
INT32 i;
char wadfilename[MAX_WADPATH];
q = &transfer[node].txlist;
while (*q)
q = &((*q)->next);
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (p)
memset(p, 0, sizeof (filetx_t));
else
I_Error("SendFile: No more ram\n");
p->filename = (char *)malloc(MAX_WADPATH);
if (!p->filename)
I_Error("SendFile: No more ram\n");
// a minimum of security, can get only file in srb2 direcory
strlcpy(p->filename, filename, MAX_WADPATH);
nameonly(p->filename);
// check first in wads loaded the majority of case
for (i = 0; wadfiles[i]; i++)
{
strlcpy(wadfilename, wadfiles[i]->filename, MAX_WADPATH);
nameonly(wadfilename);
if (!stricmp(wadfilename, p->filename))
{
// copy filename with full path
strlcpy(p->filename, wadfiles[i]->filename, MAX_WADPATH);
break;
}
}
if (!wadfiles[i])
{
DEBFILE(va("%s not found in wadfiles\n", filename));
// this formerly checked if (!findfile(p->filename, NULL, true))
// not found
// don't inform client (probably hacker)
DEBFILE(va("Client %d request %s: not found\n", node, filename));
free(p->filename);
free(p);
*q = NULL;
return;
}
if (wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024)
{
// too big
// don't inform client (client sucks, man)
DEBFILE(va("Client %d request %s: file too big, not sending\n", node, filename));
free(p->filename);
free(p);
*q = NULL;
return;
}
DEBFILE(va("Sending file %s (id=%d) to %d\n", filename, fileid, node));
p->ram = SF_FILE;
p->fileid = fileid;
p->next = NULL; // end of list
filetosend++;
}
void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
{
filetx_t **q;
......@@ -547,162 +385,7 @@ static void EndSend(INT32 node)
filetosend--;
}
#define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH)
void FiletxTicker(void)
{
static INT32 currentnode = 0;
filetx_pak *p;
size_t size;
filetx_t *f;
INT32 packetsent = PACKETPERTIC, ram, i;
if (!filetosend)
return;
if (!packetsent)
packetsent++;
// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
while (packetsent-- && filetosend != 0)
{
for (i = currentnode, ram = 0; ram < MAXNETNODES;
i = (i+1) % MAXNETNODES, ram++)
{
if (transfer[i].txlist)
goto found;
}
// no transfer to do
I_Error("filetosend=%d but no filetosend found\n", filetosend);
found:
currentnode = (i+1) % MAXNETNODES;
f = transfer[i].txlist;
ram = f->ram;
if (!transfer[i].currentfile) // file not already open
{
if (!ram)
{
long filesize;
transfer[i].currentfile =
fopen(f->filename, "rb");
if (!transfer[i].currentfile)
I_Error("File %s does not exist",
f->filename);
fseek(transfer[i].currentfile, 0, SEEK_END);
filesize = ftell(transfer[i].currentfile);
// Nobody wants to transfer a file bigger
// than 4GB!
if (filesize >= LONG_MAX)
I_Error("filesize of %s is too large", f->filename);
if (-1 == filesize)
I_Error("Error getting filesize of %s", f->filename);
f->size = (UINT32)filesize;
fseek(transfer[i].currentfile, 0, SEEK_SET);
}
else
transfer[i].currentfile = (FILE *)1;
transfer[i].position = 0;
}
p = &netbuffer->u.filetxpak;
size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE);
if (f->size-transfer[i].position < size)
size = f->size-transfer[i].position;
if (ram)
M_Memcpy(p->data, &f->filename[transfer[i].position], size);
else if (fread(p->data, 1, size, transfer[i].currentfile) != size)
I_Error("FiletxTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->filename, transfer[i].position, strerror(ferror(transfer[i].currentfile)));
p->position = LONG(transfer[i].position);
// put flag so receiver know the totalsize
if (transfer[i].position + size == f->size)
p->position |= LONG(0x80000000);
p->fileid = f->fileid;
p->size = SHORT((UINT16)size);
netbuffer->packettype = PT_FILEFRAGMENT;
if (!HSendPacket(i, true, 0, FILETXHEADER + size)) // reliable SEND
{ // not sent for some odd reason, retry at next call
if (!ram)
fseek(transfer[i].currentfile,transfer[i].position,SEEK_SET);
// exit the while (can't send this one so why should i send the next?)
break;
}
else // success
{
transfer[i].position = (UINT32)(size+transfer[i].position);
if (transfer[i].position == f->size) // finish ?
EndSend(i);
}
}
}
void Got_Filetxpak(void)
{
INT32 filenum = netbuffer->u.filetxpak.fileid;
static INT32 filetime = 0;
if (filenum >= fileneedednum)
{
DEBFILE(va("fileframent not needed %d>%d\n",filenum, fileneedednum));
return;
}
if (fileneeded[filenum].status == FS_REQUESTED)
{
if (fileneeded[filenum].phandle) I_Error("Got_Filetxpak: allready open file\n");
fileneeded[filenum].phandle = fopen(fileneeded[filenum].filename, "wb");
if (!fileneeded[filenum].phandle) I_Error("Can't create file %s: %s",fileneeded[filenum].filename, strerror(errno));
CONS_Printf("\r%s...\n",fileneeded[filenum].filename);
fileneeded[filenum].currentsize = 0;
fileneeded[filenum].status = FS_DOWNLOADING;
}
if (fileneeded[filenum].status == FS_DOWNLOADING)
{
UINT32 pos = LONG(netbuffer->u.filetxpak.position);
UINT16 size = SHORT(netbuffer->u.filetxpak.size);
// use a special tric to know when file is finished (not allways used)
// WARNING: filepak can arrive out of order so don't stop now !
if (pos & 0x80000000)
{
pos &= ~0x80000000;
fileneeded[filenum].totalsize = pos + size;
}
// we can receive packet in the wrong order, anyway all os support gaped file
fseek(fileneeded[filenum].phandle,pos,SEEK_SET);
if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].phandle)!=1)
I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].phandle)));
fileneeded[filenum].currentsize += size;
// finished?
if (fileneeded[filenum].currentsize == fileneeded[filenum].totalsize)
{
fclose(fileneeded[filenum].phandle);
fileneeded[filenum].phandle = NULL;
fileneeded[filenum].status = FS_FOUND;
CONS_Printf(M_GetText("Downloading %s...(done)\n"),
fileneeded[filenum].filename);
}
}
else
I_Error("Received a file not requested\n");
// send ack back quickly
if (++filetime == 3)
{
Net_SendAcks(servernode);
filetime = 0;
}
#ifdef CLIENT_LOADINGSCREEN
lastfilenum = filenum;
#endif
}
void AbortSendFiles(INT32 node)
static void AbortSendFiles(INT32 node)
{
while (transfer[node].txlist)
EndSend(node);
......@@ -725,7 +408,7 @@ void CloseNetFile(void)
}
// remove FILEFRAGMENT from acknledge list
Net_AbortPacketType(PT_FILEFRAGMENT);
//Net_AbortPacketType(PT_FILEFRAGMENT);
}
// functions cut and pasted from doomatic :)
......
......@@ -58,23 +58,14 @@ UINT8 *PutFileNeeded(void);
void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr);
void CL_PrepareDownloadSaveGame(const char *tmpsave);
// check file list in wadfiles return 0 when a file is not found
// 1 if all file are found
// 2 if you cannot connect (different wad version or
// no enought space to download files)
INT32 CL_CheckFiles(void);
void CL_LoadServerFiles(void);
void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod,
UINT8 fileid);
void FiletxTicker(void);
void Got_Filetxpak(void);
INT32 CL_CheckFiles(void);
boolean CL_CheckDownloadable(void);
boolean CL_SendRequestFile(void);
void Got_RequestFilePak(INT32 node);
void AbortSendFiles(INT32 node);
void CloseNetFile(void);
boolean fileexist(char *filename, time_t ptime);
......
......@@ -161,11 +161,15 @@ typedef enum
// Are animation frames playing?
PA_ETC=0,
PA_IDLE,
PA_EDGE,
PA_WALK,
PA_RUN,
PA_PAIN,
PA_ROLL,
PA_SPRING,
PA_FALL,
PA_ABILITY
PA_ABILITY,
PA_RIDE
} panim_t;
typedef enum
......