diff --git a/debian/docs b/debian/docs index f3d4ef20e..b43bf86b5 100644 --- a/debian/docs +++ b/debian/docs @@ -1,2 +1 @@ -readme.txt -readme.txt +README.md diff --git a/src/Makefile b/src/Makefile index ce4b569ee..76f013c52 100644 --- a/src/Makefile +++ b/src/Makefile @@ -179,6 +179,9 @@ endif ifdef LINUX UNIXCOMMON=1 +ifndef NOGME +HAVE_LIBGME=1 +endif endif ifdef SOLARIS @@ -315,6 +318,13 @@ LIBS+=$(PNG_LDFLAGS) CFLAGS+=$(PNG_CFLAGS) endif +ZLIB_PKGCONFIG?=zlib +ZLIB_CFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --cflags) +ZLIB_LDFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --libs) + +LIBS+=$(ZLIB_LDFLAGS) +CFLAGS+=$(ZLIB_CFLAGS) + ifdef HAVE_LIBGME OPTS+=-DHAVE_LIBGME @@ -366,6 +376,14 @@ endif OPTS:=-fno-exceptions $(OPTS) +ifdef MOBJCONSISTANCY + OPTS+=-DMOBJCONSISTANCY +endif + +ifdef PACKETDROP + OPTS+=-DPACKETDROP +endif + ifdef DEBUGMODE # build with debugging information @@ -375,7 +393,7 @@ ifdef GCC48 else CFLAGS+=-O0 endif - CFLAGS+= -Wall -DPARANOIA -DRANGECHECK + CFLAGS+= -Wall -DPARANOIA -DRANGECHECK -DPACKETDROP -DMOBJCONSISTANCY else diff --git a/src/android/i_system.c b/src/android/i_system.c index 150cbd505..58fca7c19 100644 --- a/src/android/i_system.c +++ b/src/android/i_system.c @@ -258,6 +258,18 @@ INT32 I_PutEnv(char *variable) return -1; } +INT32 I_ClipboardCopy(const char *data, size_t size) +{ + (void)data; + (void)size; + return -1; +} + +char *I_ClipboardPaste(void) +{ + return NULL; +} + void I_RegisterSysCommands(void) {} #include "../sdl/dosstr.c" diff --git a/src/console.c b/src/console.c index 025bc1c19..70f8ab6f8 100644 --- a/src/console.c +++ b/src/console.c @@ -84,19 +84,23 @@ UINT32 con_scalefactor; // text size scale factor // hold 32 last lines of input for history #define CON_MAXPROMPTCHARS 256 -#define CON_PROMPTCHAR '>' +#define CON_PROMPTCHAR '$' static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines static INT32 inputline; // current input line number static INT32 inputhist; // line number of history input line to restore -static size_t input_cx; // position in current input line +static size_t input_cur; // position of cursor in line +static size_t input_sel; // position of selection marker (I.E.: anything between this and input_cur is "selected") +static size_t input_len; // length of current line, used to bound cursor and such +// notice: input does NOT include the "$" at the start of the line. - 11/3/16 // protos. static void CON_InputInit(void); static void CON_RecalcSize(void); static void CONS_hudlines_Change(void); +static void CONS_backcolor_Change(void); static void CON_DrawBackpic(patch_t *pic, INT32 startx, INT32 destwidth); //static void CON_DrawBackpic2(pic_t *pic, INT32 startx, INT32 destwidth); @@ -129,10 +133,11 @@ static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"} // whether to use console background picture, or translucent mode static consvar_t cons_backpic = {"con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Orange"}, - {2, "Blue"}, {3, "Green"}, {4, "Gray"}, - {5, "Red"}, {0, NULL}}; -consvar_t cons_backcolor = {"con_backcolor", "3", CV_SAVE, backcolor_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Gray"}, {2, "Brown"}, + {3, "Red"}, {4, "Orange"}, {5, "Yellow"}, + {6, "Green"}, {7, "Blue"}, {8, "Cyan"}, + {0, NULL}}; +consvar_t cons_backcolor = {"con_backcolor", "Green", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL}; static void CON_Print(char *msg); @@ -219,8 +224,9 @@ static void CONS_Bind_f(void) // CONSOLE SETUP //====================================================================== -// Prepare a colormap for GREEN ONLY translucency over background -// +// Font colormap colors +// TODO: This could probably be improved somehow... +// These colormaps are 99% identical, with just a few changed bytes UINT8 *yellowmap; UINT8 *purplemap; UINT8 *lgreenmap; @@ -229,44 +235,49 @@ UINT8 *graymap; UINT8 *redmap; UINT8 *orangemap; -// Console BG colors -UINT8 *cwhitemap; -UINT8 *corangemap; -UINT8 *cbluemap; -UINT8 *cgreenmap; -UINT8 *cgraymap; -UINT8 *credmap; +// Console BG color +UINT8 *consolebgmap = NULL; -void CON_ReSetupBackColormap(UINT16 num) +void CON_SetupBackColormap(void) { - UINT16 i, j; - UINT8 k; - UINT8 *pal = W_CacheLumpName(R_GetPalname(num), PU_CACHE); + UINT16 i, palsum; + UINT8 j, palindex; + UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE); - // setup the green translucent background colormaps - for (i = 0, k = 0; i < 768; i += 3, k++) + if (!consolebgmap) + consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); + + switch (cons_backcolor.value) { - 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)); - cgraymap[k] = (UINT8)(31 - (j>>6)); - credmap[k] = (UINT8)(143 - (j>>6)); + case 0: palindex = 15; break; // White + case 1: palindex = 31; break; // Gray + case 2: palindex = 63; break; // Brown + case 3: palindex = 143; break; // Red + case 4: palindex = 95; break; // Orange + case 5: palindex = 111; break; // Yellow + case 6: palindex = 175; break; // Green + case 7: palindex = 239; break; // Blue + case 8: palindex = 219; break; // Cyan + // Default green + default: palindex = 175; break; +} + + // setup background colormap + for (i = 0, j = 0; i < 768; i += 3, j++) + { + palsum = (pal[i] + pal[i+1] + pal[i+2]) >> 6; + consolebgmap[j] = (UINT8)(palindex - palsum); } } -static void CON_SetupBackColormap(void) +static void CONS_backcolor_Change(void) { - INT32 i, j, k; - UINT8 *pal; + CON_SetupBackColormap(); +} - cwhitemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); - corangemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); - cbluemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); - cgreenmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); - cgraymap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); - credmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); +static void CON_SetupColormaps(void) +{ + INT32 i; yellowmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); graymap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); @@ -276,20 +287,6 @@ static void CON_SetupBackColormap(void) redmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); orangemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); - pal = W_CacheLumpName("PLAYPAL", PU_CACHE); - - // setup the green translucent background colormaps - for (i = 0, k = 0; i < 768; i += 3, k++) - { - 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)); - cgraymap[k] = (UINT8)(31 - (j>>6)); - credmap[k] = (UINT8)(143 - (j>>6)); - } - // setup the other colormaps, for console text // these don't need to be aligned, unless you convert the @@ -320,6 +317,9 @@ static void CON_SetupBackColormap(void) redmap[9] = (UINT8)127; orangemap[3] = (UINT8)85; orangemap[9] = (UINT8)90; + + // Init back colormap + CON_SetupBackColormap(); } // Setup the console text buffer @@ -343,7 +343,7 @@ void CON_Init(void) con_width = 0; CON_RecalcSize(); - CON_SetupBackColormap(); + CON_SetupColormaps(); //note: CON_Ticker should always execute at least once before D_Display() con_clipviewtop = -1; // -1 does not clip @@ -386,14 +386,10 @@ void CON_Init(void) // static void CON_InputInit(void) { - INT32 i; - // prepare the first prompt line memset(inputlines, 0, sizeof (inputlines)); - for (i = 0; i < 32; i++) - inputlines[i][0] = CON_PROMPTCHAR; inputline = 0; - input_cx = 1; + input_cur = input_sel = input_len = 0; } //====================================================================== @@ -618,13 +614,91 @@ void CON_Ticker(void) } } +// +// ---- +// +// Shortcuts for adding and deleting characters, strings, and sections +// Necessary due to moving cursor +// + +static void CON_InputClear(void) +{ + memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS); + input_cur = input_sel = input_len = 0; +} + +static void CON_InputSetString(const char *c) +{ + memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS); + strcpy(inputlines[inputline], c); + input_cur = input_sel = input_len = strlen(c); +} + +static void CON_InputAddString(const char *c) +{ + size_t csize = strlen(c); + if (input_len + csize > CON_MAXPROMPTCHARS-1) + return; + if (input_cur != input_len) + memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur); + memcpy(&inputlines[inputline][input_cur], c, csize); + input_len += csize; + input_sel = (input_cur += csize); +} + +static void CON_InputDelSelection(void) +{ + size_t start, end, len; + if (input_cur > input_sel) + { + start = input_sel; + end = input_cur; + } + else + { + start = input_cur; + end = input_sel; + } + len = (end - start); + + if (end != input_len) + memmove(&inputlines[inputline][start], &inputlines[inputline][end], input_len-end); + memset(&inputlines[inputline][input_len - len], 0, len); + + input_len -= len; + input_sel = input_cur = start; +} + +static void CON_InputAddChar(char c) +{ + if (input_len >= CON_MAXPROMPTCHARS-1) + return; + if (input_cur != input_len) + memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur); + inputlines[inputline][input_cur++] = c; + inputlines[inputline][++input_len] = 0; + input_sel = input_cur; +} + +static void CON_InputDelChar(void) +{ + if (!input_cur) + return; + if (input_cur != input_len) + memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur); + inputlines[inputline][--input_len] = 0; + input_sel = --input_cur; +} + +// +// ---- +// + // Handles console key input // boolean CON_Responder(event_t *ev) { - static boolean consdown; - static boolean shiftdown; - static boolean ctrldown; + static UINT8 consdown = false; // console is treated differently due to rare usage // sequential completions a la 4dos static char completion[80]; @@ -639,13 +713,8 @@ boolean CON_Responder(event_t *ev) // let go keyup events, don't eat them if (ev->type != ev_keydown && ev->type != ev_console) { - if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT) - shiftdown = false; - else if (ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL) - ctrldown = false; - else if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1]) + if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1]) consdown = false; - return false; } @@ -684,94 +753,110 @@ boolean CON_Responder(event_t *ev) consoletoggle = true; return true; } - } - // eat shift only if console active - if (key == KEY_LSHIFT || key == KEY_RSHIFT) - { - shiftdown = true; + // Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas + if (key == KEY_LSHIFT || key == KEY_RSHIFT + || key == KEY_LCTRL || key == KEY_RCTRL + || key == KEY_LALT || key == KEY_RALT) return true; - } - // same for ctrl - if (key == KEY_LCTRL || key == KEY_RCTRL) + // ctrl modifier -- changes behavior, adds shortcuts + if (ctrldown) { - ctrldown = true; - return true; + // show all cvars/commands that match what we have inputted + if (key == KEY_TAB) + { + size_t i, len; + + if (!completion[0]) + { + if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' ')) + return true; + strcpy(completion, inputlines[inputline]); + comskips = varskips = 0; + } + len = strlen(completion); + + //first check commands + CONS_Printf("\nCommands:\n"); + for (i = 0, cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, ++i)) + CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len); + if (i == 0) CONS_Printf(" (none)\n"); + + //now we move on to CVARs + CONS_Printf("Variables:\n"); + for (i = 0, cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, ++i)) + CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len); + if (i == 0) CONS_Printf(" (none)\n"); + + return true; + } + // --- + + if (key == KEY_HOME) // oldest text in buffer + { + con_scrollup = (con_totallines-((con_curlines-16)>>3)); + return true; + } + else if (key == KEY_END) // most recent text in buffer + { + con_scrollup = 0; + return true; + } + + if (key == 'x' || key == 'X') + { + if (input_sel > input_cur) + I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur); + else + I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel); + CON_InputDelSelection(); + completion[0] = 0; + return true; + } + else if (key == 'c' || key == 'C') + { + if (input_sel > input_cur) + I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur); + else + I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel); + return true; + } + else if (key == 'v' || key == 'V') + { + const char *paste = I_ClipboardPaste(); + if (input_sel != input_cur) + CON_InputDelSelection(); + if (paste != NULL) + CON_InputAddString(paste); + completion[0] = 0; + return true; + } + + // Select all + if (key == 'a' || key == 'A') + { + input_sel = 0; + input_cur = input_len; + return true; + } + + // don't eat the key + return false; } // command completion forward (tab) and backward (shift-tab) if (key == KEY_TAB) { - // show all cvars/commands that match what we have inputted - if (ctrldown) - { - UINT32 i; - size_t stop = input_cx - 1; - char nameremainder[255]; - - if (input_cx < 2 || strlen(inputlines[inputline]+1) >= 80) - return true; - - strcpy(completion, inputlines[inputline]+1); - - // trimming: stop at the first newline - for (i = 0; i < input_cx - 1; ++i) - { - if (completion[i] == ' ') - { - completion[i] = '\0'; - stop = i; - break; - } - } - - i = 0; - - //first check commands - CONS_Printf("\nCommands:\n"); - - for (cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, i)) - { - strncpy(nameremainder, cmd+(stop), strlen(cmd)-(stop)); - nameremainder[strlen(cmd)-(stop)] = '\0'; - - CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, nameremainder); - ++i; - } - if (i == 0) - CONS_Printf(" (none)\n"); - - i = 0; - - //now we move on to CVARs - CONS_Printf("Variables:\n"); - - for (cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, i)) - { - strncpy(nameremainder, cmd+(stop), strlen(cmd)-(stop)); - nameremainder[strlen(cmd)-(stop)] = '\0'; - - CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, nameremainder); - ++i; - } - if (i == 0) - CONS_Printf(" (none)\n"); - - return true; - } - // sequential command completion forward and backward // remember typing for several completions (a-la-4dos) - if (inputlines[inputline][input_cx-1] != ' ') + if (!completion[0]) { - if (strlen(inputlines[inputline]+1) < 80) - strcpy(completion, inputlines[inputline]+1); - else - completion[0] = 0; - + if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' ')) + return true; + strcpy(completion, inputlines[inputline]); comskips = varskips = 0; } else @@ -783,37 +868,26 @@ boolean CON_Responder(event_t *ev) if (--varskips < 0) comskips = -comskips - 2; } - else if (comskips > 0) - comskips--; + else if (comskips > 0) comskips--; } else { - if (comskips < 0) - varskips++; - else - comskips++; + if (comskips < 0) varskips++; + else comskips++; } } if (comskips >= 0) { cmd = COM_CompleteCommand(completion, comskips); - if (!cmd) - // dirty: make sure if comskips is zero, to have a neg value + if (!cmd) // dirty: make sure if comskips is zero, to have a neg value comskips = -comskips - 1; } if (comskips < 0) cmd = CV_CompleteVar(completion, varskips); if (cmd) - { - memset(inputlines[inputline]+1, 0, CON_MAXPROMPTCHARS-1); - strcpy(inputlines[inputline]+1, cmd); - input_cx = strlen(cmd) + 1; - inputlines[inputline][input_cx] = ' '; - input_cx++; - inputlines[inputline][input_cx] = 0; - } + CON_InputSetString(va("%s ", cmd)); else { if (comskips > 0) @@ -839,47 +913,80 @@ boolean CON_Responder(event_t *ev) return true; } - if (key == KEY_HOME) // oldest text in buffer + if (key == KEY_LEFTARROW) { - con_scrollup = (con_totallines-((con_curlines-16)>>3)); + if (input_cur != 0) + --input_cur; + if (!shiftdown) + input_sel = input_cur; return true; } - else if (key == KEY_END) // most recent text in buffer + else if (key == KEY_RIGHTARROW) { - con_scrollup = 0; + if (input_cur < input_len) + ++input_cur; + if (!shiftdown) + input_sel = input_cur; return true; } + else if (key == KEY_HOME) + { + input_cur = 0; + if (!shiftdown) + input_sel = input_cur; + return true; + } + else if (key == KEY_END) + { + input_cur = input_len; + if (!shiftdown) + input_sel = input_cur; + return true; + } + + // At this point we're messing with input + // Clear completion + completion[0] = 0; // command enter if (key == KEY_ENTER) { - if (input_cx < 2) + if (!input_len) return true; // push the command - COM_BufAddText(inputlines[inputline]+1); + COM_BufAddText(inputlines[inputline]); COM_BufAddText("\n"); - CONS_Printf("%s\n", inputlines[inputline]); + CONS_Printf("\x86""%c""\x80""%s\n", CON_PROMPTCHAR, inputlines[inputline]); inputline = (inputline+1) & 31; inputhist = inputline; - - memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS); - inputlines[inputline][0] = CON_PROMPTCHAR; - input_cx = 1; + CON_InputClear(); return true; } - // backspace command prompt - if (key == KEY_BACKSPACE) + // backspace and delete command prompt + if (input_sel != input_cur) { - if (input_cx > 1) + if (key == KEY_BACKSPACE || key == KEY_DEL) { - input_cx--; - inputlines[inputline][input_cx] = 0; + CON_InputDelSelection(); + return true; } + } + else if (key == KEY_BACKSPACE) + { + CON_InputDelChar(); + return true; + } + else if (key == KEY_DEL) + { + if (input_cur == input_len) + return true; + ++input_cur; + CON_InputDelChar(); return true; } @@ -888,18 +995,15 @@ boolean CON_Responder(event_t *ev) { // copy one of the previous inputlines to the current do - { inputhist = (inputhist - 1) & 31; // cycle back - } while (inputhist != inputline && !inputlines[inputhist][1]); + while (inputhist != inputline && !inputlines[inputhist][0]); // stop at the last history input line, which is the // current line + 1 because we cycle through the 32 input lines if (inputhist == inputline) inputhist = (inputline + 1) & 31; - M_Memcpy(inputlines[inputline], inputlines[inputhist], CON_MAXPROMPTCHARS); - input_cx = strlen(inputlines[inputline]); - + CON_InputSetString(inputlines[inputhist]); return true; } @@ -909,23 +1013,14 @@ boolean CON_Responder(event_t *ev) if (inputhist == inputline) return true; do - { inputhist = (inputhist + 1) & 31; - } while (inputhist != inputline && !inputlines[inputhist][1]); - - memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS); + while (inputhist != inputline && !inputlines[inputhist][0]); // back to currentline if (inputhist == inputline) - { - inputlines[inputline][0] = CON_PROMPTCHAR; - input_cx = 1; - } + CON_InputClear(); else - { - strcpy(inputlines[inputline], inputlines[inputhist]); - input_cx = strlen(inputlines[inputline]); - } + CON_InputSetString(inputlines[inputhist]); return true; } @@ -950,15 +1045,12 @@ boolean CON_Responder(event_t *ev) return false; // add key to cmd line here - if (input_cx < CON_MAXPROMPTCHARS) - { - if (key >= 'A' && key <= 'Z' && !shiftdown) //this is only really necessary for dedicated servers - key = key + 'a' - 'A'; + if (key >= 'A' && key <= 'Z' && !shiftdown) //this is only really necessary for dedicated servers + key = key + 'a' - 'A'; - inputlines[inputline][input_cx] = (char)key; - inputlines[inputline][input_cx + 1] = 0; - input_cx++; - } + if (input_sel != input_cur) + CON_InputDelSelection(); + CON_InputAddChar(key); return true; } @@ -1242,26 +1334,89 @@ void CONS_Error(const char *msg) // static void CON_DrawInput(void) { - char *p; - size_t c; - INT32 x, y; INT32 charwidth = (INT32)con_scalefactor << 3; - - // input line scrolls left if it gets too long - p = inputlines[inputline]; - if (input_cx >= con_width-11) - p += input_cx - (con_width-11) + 1; + const char *p = inputlines[inputline]; + size_t c, clen, cend; + UINT8 lellip = 0, rellip = 0; + INT32 x, y, i; y = con_curlines - 12 * con_scalefactor; + x = charwidth*2; - for (c = 0, x = charwidth; c < con_width-11; c++, x += charwidth) - V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value); + clen = con_width-13; - // draw the blinking cursor - // - x = ((input_cx >= con_width-11) ? (INT32)(con_width-11) : (INT32)((input_cx + 1)) * charwidth); - if (con_tick < 4) - V_DrawCharacter(x, y, '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value); + if (input_len <= clen) + { + c = 0; + clen = input_len; + } + else // input line scrolls left if it gets too long + { + clen -= 2; // There will always be some extra truncation -- but where is what we'll find out + + if (input_cur <= clen/2) + { + // Close enough to right edge to show all + c = 0; + // Always will truncate right side from this position, so always draw right ellipsis + rellip = 1; + } + else + { + // Cursor in the middle (or right side) of input + // Move over for the ellipsis + c = input_cur - (clen/2) + 2; + x += charwidth*2; + lellip = 1; + + if (c + clen >= input_len) + { + // Cursor in the right side of input + // We were too far over, so move back + c = input_len - clen; + } + else + { + // Cursor in the middle -- ellipses on both sides + clen -= 2; + rellip = 1; + } + } + } + + if (lellip) + { + x -= charwidth*3; + if (input_sel < c) + V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 107 | V_NOSCALESTART); + for (i = 0; i < 3; ++i, x += charwidth) + V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value); + } + else + V_DrawCharacter(x-charwidth, y, CON_PROMPTCHAR | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value); + + for (cend = c + clen; c < cend; ++c, x += charwidth) + { + if ((input_sel > c && input_cur <= c) || (input_sel <= c && input_cur > c)) + { + V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 107 | V_NOSCALESTART); + V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, !cv_allcaps.value); + } + else + V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value); + + if (c == input_cur && con_tick >= 4) + V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value); + } + if (cend == input_cur && con_tick >= 4) + V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value); + if (rellip) + { + if (input_sel > cend) + V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 107 | V_NOSCALESTART); + for (i = 0; i < 3; ++i, x += charwidth) + V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value); + } } // draw the last lines of console text to the top of the screen @@ -1417,7 +1572,7 @@ static void CON_DrawConsole(void) { // inu: no more width (was always 0 and vid.width) if (rendermode != render_none) - V_DrawFadeConsBack(con_curlines, cons_backcolor.value); // translucent background + V_DrawFadeConsBack(con_curlines); // translucent background } // draw console text lines from top to bottom diff --git a/src/console.h b/src/console.h index 47af65e21..8cf6483ff 100644 --- a/src/console.h +++ b/src/console.h @@ -40,11 +40,10 @@ extern consvar_t cons_backcolor; extern UINT8 *yellowmap, *purplemap, *lgreenmap, *bluemap, *graymap, *redmap, *orangemap; -// Console bg colors: -extern UINT8 *cwhitemap, *corangemap, *cbluemap, *cgreenmap, *cgraymap, - *credmap; +// Console bg color (auto updated to match) +extern UINT8 *consolebgmap; -void CON_ReSetupBackColormap(UINT16 num); +void CON_SetupBackColormap(void); void CON_ClearHUD(void); // clear heads up messages void CON_Ticker(void); diff --git a/src/d_clisrv.c b/src/d_clisrv.c index c0f81ba32..7e9dcf8ef 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -58,14 +58,14 @@ // 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: +// Server: +// maketic is the tic that hasn't had control made for it yet // 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 +// Client: +// neededtic is the tic needed by the client to run the game // firstticstosend is used to optimize a condition -// normally maketic >= gametic > 0 +// Normally maketic >= gametic > 0 #define PREDICTIONQUEUE BACKUPTICS #define PREDICTIONMASK (PREDICTIONQUEUE-1) @@ -151,12 +151,6 @@ static consvar_t cv_showjoinaddress = {"showjoinaddress", "On", 0, CV_OnOff, NUL static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; consvar_t cv_playbackspeed = {"playbackspeed", "1", 0, playbackspeed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -void D_ResetTiccmds(void) -{ - memset(&localcmds, 0, sizeof(ticcmd_t)); - memset(&localcmds2, 0, sizeof(ticcmd_t)); -} - static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n) { const size_t d = n / sizeof(ticcmd_t); @@ -185,11 +179,17 @@ static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n) -// some software don't support largest packet -// (original sersetup, not exactely, but the probabylity of sending a packet -// of 512 octet is like 0.1) +// Some software don't support largest packet +// (original sersetup, not exactely, but the probability of sending a packet +// of 512 bytes is like 0.1) UINT16 software_MAXPACKETLENGTH; +/** Guesses the value of a tic from its lowest byte and from maketic + * + * \param low The lowest byte of the tic value + * \return The full tic value + * + */ tic_t ExpandTics(INT32 low) { INT32 delta; @@ -214,7 +214,7 @@ void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)) { #ifdef PARANOIA if (id >= MAXNETXCMD) - I_Error("command id %d too big", id); + I_Error("Command id %d too big", id); if (listnetxcmd[id] != 0) I_Error("Command id %d already used", id); #endif @@ -378,7 +378,7 @@ static void ExtraDataTicker(void) { const UINT8 id = *curpos; curpos++; - DEBFILE(va("executing x_cmd %u ply %u ", id, i)); + DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i)); (listnetxcmd[id])(&curpos, i); DEBFILE("done\n"); } @@ -401,7 +401,11 @@ static void ExtraDataTicker(void) } } - D_FreeTextcmd(gametic); + // If you are a client, you can safely forget the net commands for this tic + // If you are the server, you need to remember them until every client has been aknowledged, + // because if you need to resend a PT_SERVERTICS packet, you need to put the commands in it + if (!server) + D_FreeTextcmd(gametic); } static void D_Clearticcmd(tic_t tic) @@ -416,6 +420,19 @@ static void D_Clearticcmd(tic_t tic) DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS)); } +void D_ResetTiccmds(void) +{ + INT32 i; + + memset(&localcmds, 0, sizeof(ticcmd_t)); + memset(&localcmds2, 0, sizeof(ticcmd_t)); + + // Reset the net command list + for (i = 0; i < TEXTCMD_HASH_SIZE; i++) + while (textcmds[i]) + D_Clearticcmd(textcmds[i]->tic); +} + // ----------------------------------------------------------------- // end of extra data function // ----------------------------------------------------------------- @@ -1034,20 +1051,20 @@ static INT16 Consistancy(void); typedef enum { - cl_searching, - cl_downloadfiles, - cl_askjoin, - cl_waitjoinresponse, + CL_SEARCHING, + CL_DOWNLOADFILES, + CL_ASKJOIN, + CL_WAITJOINRESPONSE, #ifdef JOININGAME - cl_downloadsavegame, + CL_DOWNLOADSAVEGAME, #endif - cl_connected, - cl_aborted + CL_CONNECTED, + CL_ABORTED } cl_mode_t; static void GetPackets(void); -static cl_mode_t cl_mode = cl_searching; +static cl_mode_t cl_mode = CL_SEARCHING; // Player name send/load @@ -1100,10 +1117,10 @@ 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) + 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) ? 128 : 160; // 15 pal entries total. const char *cltext; @@ -1113,7 +1130,7 @@ static inline void CL_DrawConnectionStatus(void) switch (cl_mode) { #ifdef JOININGAME - case cl_downloadsavegame: + case CL_DOWNLOADSAVEGAME: cltext = M_GetText("Downloading game state..."); Net_GetNetStat(); V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE, @@ -1122,8 +1139,8 @@ static inline void CL_DrawConnectionStatus(void) va("%3.1fK/s ", ((double)getbps)/1024)); break; #endif - case cl_askjoin: - case cl_waitjoinresponse: + case CL_ASKJOIN: + case CL_WAITJOINRESPONSE: cltext = M_GetText("Requesting to join..."); break; default: @@ -1157,11 +1174,16 @@ static inline void CL_DrawConnectionStatus(void) } #endif -// -// CL_SendJoin -// -// send a special packet for declare how many player in local -// used only in arbitratrenetstart() +/** Sends a special packet to declare how many players in local + * Used only in arbitratrenetstart() + * Sends a PT_CLIENTJOIN packet to the server + * + * \return True if the packet was successfully sent + * \todo Improve the description... + * Because to be honest, I have no idea what arbitratrenetstart is... + * Is it even used...? + * + */ static boolean CL_SendJoin(void) { UINT8 localplayers = 1; @@ -1296,6 +1318,12 @@ static void SV_SendPlayerInfo(INT32 node) HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS); } +/** Sends a PT_SERVERCFG packet + * + * \param node The destination + * \return True if the packet was successfully sent + * + */ static boolean SV_SendServerConfig(INT32 node) { INT32 i; @@ -1428,7 +1456,7 @@ static void SV_SendSaveGame(INT32 node) WRITEUINT32(savebuffer, 0); } - SendRam(node, buffertosend, length, SF_RAM, 0); + SV_SendRam(node, buffertosend, length, SF_RAM, 0); save_p = NULL; } @@ -1523,7 +1551,7 @@ static void CL_LoadReceivedSavegame(void) { CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl); if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - CONS_Printf(M_GetText("ZONE")); + CONS_Printf(M_GetText(" ZONE")); if (actnum > 0) CONS_Printf(" %2d", actnum); } @@ -1679,11 +1707,252 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) #endif // ifndef NONET -// use adaptive send using net_bandwidth and stat.sendbytes +/** Called by CL_ServerConnectionTicker + * + * \param viams ??? + * \param asksent ??? + * \return False if the connection was aborted + * \sa CL_ServerConnectionTicker + * \sa CL_ConnectToServer + * + */ +static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) +{ +#ifndef NONET + INT32 i; +#endif + +#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) + return true; + } + + // 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 false; + } + + 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 false; + } + 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 connect 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 false; + } + // 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. + + return true; + } + + // Ask the info to the server (askinfo packet) + if (*asksent + NEWTICRATE < I_GetTime()) + { + SendAskInfo(servernode, viams); + *asksent = I_GetTime(); + } +#else + (void)viams; + (void)asksent; + // No netgames, so we skip this state. + cl_mode = CL_ASKJOIN; +#endif // ifndef NONET/else + + return true; +} + +/** Called by CL_ConnectToServer + * + * \param viams ??? + * \param tmpsave The name of the gamestate file??? + * \param oldtic Used for knowing when to poll events and redraw + * \param asksent ??? + * \return False if the connection was aborted + * \sa CL_ServerConnectionSearchTicker + * \sa CL_ConnectToServer + * + */ +static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic_t *oldtic, tic_t *asksent) +{ + boolean waitmore; + INT32 i; + +#ifdef NONET + (void)tmpsave; +#endif + + switch (cl_mode) + { + case CL_SEARCHING: + if (!CL_ServerConnectionSearchTicker(viams, asksent)) + return false; + break; + + case CL_DOWNLOADFILES: + waitmore = false; + for (i = 0; i < fileneedednum; i++) + if (fileneeded[i].status == FS_DOWNLOADING + || fileneeded[i].status == FS_REQUESTED) + { + waitmore = true; + break; + } + if (waitmore) + break; // exit the case + + 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; + break; + +#ifdef JOININGAME + case CL_DOWNLOADSAVEGAME: + // At this state, the first (and only) needed file is the gamestate + 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: + break; + + // Connection closed by cancel, timeout or refusal. + case CL_ABORTED: + cl_mode = CL_SEARCHING; + return false; + + } + + GetPackets(); + Net_AckTicker(); + + // Call it only once by tic + if (*oldtic != I_GetTime()) + { + INT32 key; + + I_OsPolling(); + key = I_GetKey(); + if (key == KEY_ESCAPE) + { + CONS_Printf(M_GetText("Network game synchronization aborted.\n")); +// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + return false; + } + + // why are these here? this is for servers, we're a client + //if (key == 's' && server) + // doomcom->numnodes = (INT16)pnumnodes; + //SV_FileSendTicker(); + *oldtic = I_GetTime(); + +#ifdef CLIENT_LOADINGSCREEN + if (!server && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED) + { + F_TitleScreenTicker(true); + F_TitleScreenDrawer(); + CL_DrawConnectionStatus(); + I_UpdateNoVsync(); // page flip or blit buffer + if (moviemode) + M_SaveFrame(); + } +#else + CON_Drawer(); + I_UpdateNoVsync(); +#endif + } + else + I_Sleep(); + + return true; +} + +/** Use adaptive send using net_bandwidth and stat.sendbytes + * + * \param viams ??? + * \todo Better description... + * + */ static void CL_ConnectToServer(boolean viams) { INT32 pnumnodes, nodewaited = doomcom->numnodes, i; - boolean waitmore; tic_t oldtic; #ifndef NONET tic_t asksent; @@ -1694,14 +1963,14 @@ static void CL_ConnectToServer(boolean viams) sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); #endif - cl_mode = cl_searching; + cl_mode = CL_SEARCHING; #ifdef CLIENT_LOADINGSCREEN lastfilenum = 0; #endif #ifdef JOININGAME - // don't get a corrupt savegame error because tmpsave already exists + // 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 @@ -1725,7 +1994,7 @@ static void CL_ConnectToServer(boolean viams) pnumnodes = 1; oldtic = I_GetTime() - 1; #ifndef NONET - asksent = (tic_t)-TICRATE; + asksent = (tic_t) - TICRATE; i = SL_SearchServer(servernode); @@ -1752,197 +2021,23 @@ static void CL_ConnectToServer(boolean viams) do { - switch (cl_mode) - { - case cl_searching: + // If the connection was aborted for some reason, leave #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(); - } + if (!CL_ServerConnectionTicker(viams, tmpsave, &oldtic, &asksent)) #else - (void)viams; - // No netgames, so we skip this state. - cl_mode = cl_askjoin; -#endif // ifndef NONET/else - break; - case cl_downloadfiles: - waitmore = false; - for (i = 0; i < fileneedednum; i++) - if (fileneeded[i].status == FS_DOWNLOADING - || fileneeded[i].status == FS_REQUESTED) - { - waitmore = true; - break; - } - if (waitmore) - break; // exit the case - - 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); + if (!CL_ServerConnectionTicker(viams, (char*)NULL, &oldtic, (tic_t *)NULL)) #endif - if (CL_SendJoin()) - cl_mode = 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: - break; - - // Connection closed by cancel, timeout or refusal. - case cl_aborted: - cl_mode = cl_searching; - return; - } - - GetPackets(); - Net_AckTicker(); - - // call it only one by tic - if (oldtic != I_GetTime()) - { - INT32 key; - - I_OsPolling(); - key = I_GetKey(); - if (key == KEY_ESCAPE) - { - CONS_Printf(M_GetText("Network game synchronization aborted.\n")); -// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING); - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - return; - } - - // why are these here? this is for servers, we're a client - //if (key == 's' && server) - // doomcom->numnodes = (INT16)pnumnodes; - //FiletxTicker(); - oldtic = I_GetTime(); - -#ifdef CLIENT_LOADINGSCREEN - if (!server && cl_mode != cl_connected && cl_mode != cl_aborted) - { - F_TitleScreenTicker(true); - F_TitleScreenDrawer(); - CL_DrawConnectionStatus(); - I_UpdateNoVsync(); // page flip or blit buffer - if (moviemode) - M_SaveFrame(); - } -#else - CON_Drawer(); - I_UpdateNoVsync(); -#endif - } - else I_Sleep(); + return; if (server) { pnumnodes = 0; for (i = 0; i < MAXNETNODES; i++) - if (nodeingame[i]) pnumnodes++; + if (nodeingame[i]) + pnumnodes++; } } - while (!(cl_mode == cl_connected && (!server || (server && nodewaited <= pnumnodes)))); + while (!(cl_mode == CL_CONNECTED && (!server || (server && nodewaited <= pnumnodes)))); DEBFILE(va("Synchronisation Finished\n")); @@ -2199,7 +2294,6 @@ void CL_ClearPlayer(INT32 playernum) P_RemoveMobj(players[playernum].mo->tracer); P_RemoveMobj(players[playernum].mo); } - players[playernum].mo = NULL; memset(&players[playernum], 0, sizeof (player_t)); } @@ -2711,6 +2805,10 @@ void D_ClientServerInit(void) COM_AddCommand("reloadbans", Command_ReloadBan); COM_AddCommand("connect", Command_connect); COM_AddCommand("nodes", Command_Nodes); +#ifdef PACKETDROP + COM_AddCommand("drop", Command_Drop); + COM_AddCommand("droprate", Command_Droprate); +#endif #endif RegisterNetXCmd(XD_KICK, Got_KickCmd); @@ -2754,7 +2852,7 @@ void SV_ResetServer(void) // +1 because this command will be executed in com_executebuffer in // tryruntic so gametic will be incremented, anyway maketic > gametic - // is not a issue + // is not an issue maketic = gametic + 1; neededtic = maketic; @@ -2808,7 +2906,7 @@ static inline void SV_GenContext(void) for (i = 0; i < 8; i++) { const char a = M_RandomKey(26*2); - if (a <= 26) // uppercase + if (a < 26) // uppercase server_context[i] = 'A'+a; else // lowercase server_context[i] = 'a'+(a-26); @@ -2843,7 +2941,7 @@ void D_QuitNetGame(void) if (serverrunning && ms_RoomId > 0) UnregisterServer(); } - else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]!=0) + else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]) { netbuffer->packettype = PT_CLIENTQUIT; HSendPacket(servernode, true, 0, 0); @@ -2864,12 +2962,12 @@ void D_QuitNetGame(void) #endif } -// add a node to the game (player will follow at map change or at savegame....) +// Adds a node to the game (player will follow at map change or at savegame....) static inline void SV_AddNode(INT32 node) { nettics[node] = gametic; supposedtics[node] = gametic; - // little hack because the server connect to itself and put + // little hack because the server connects to itself and puts // nodeingame when connected not here if (node) nodeingame[node] = true; @@ -3019,7 +3117,7 @@ static boolean SV_AddWaitingPlayers(void) void CL_AddSplitscreenPlayer(void) { - if (cl_mode == cl_connected) + if (cl_mode == CL_CONNECTED) CL_SendJoin(); } @@ -3027,7 +3125,7 @@ void CL_RemoveSplitscreenPlayer(void) { XBOXSTATIC UINT8 buf[2]; - if (cl_mode != cl_connected) + if (cl_mode != CL_CONNECTED) return; buf[0] = (UINT8)secondarydisplayplayer; @@ -3038,7 +3136,7 @@ void CL_RemoveSplitscreenPlayer(void) // is there a game running boolean Playing(void) { - return (server && serverrunning) || (!server && cl_mode == cl_connected); + return (server && serverrunning) || (!server && cl_mode == CL_CONNECTED); } boolean SV_SpawnServer(void) @@ -3086,7 +3184,7 @@ void SV_StopServer(void) D_Clearticcmd(i); consoleplayer = 0; - cl_mode = cl_searching; + cl_mode = CL_SEARCHING; maketic = gametic+1; neededtic = maketic; serverrunning = false; @@ -3132,6 +3230,11 @@ static size_t TotalTextCmdPerTic(tic_t tic) return total; } +/** Called when a PT_CLIENTJOIN packet is received + * + * \param node The packet sender + * + */ static void HandleConnect(SINT8 node) { if (bannednode && bannednode[node]) @@ -3163,6 +3266,9 @@ static void HandleConnect(SINT8 node) #endif SV_AddNode(node); + /// \note Wait what??? + /// What if the gamestate takes more than one second to get downloaded? + /// Or if a lagspike happens? // you get a free second before desynch checks. use it wisely. SV_InitResynchVars(node); @@ -3171,6 +3277,7 @@ static void HandleConnect(SINT8 node) if (!SV_SendServerConfig(node)) { G_SetGamestate(backupstate); + /// \note Shouldn't SV_SendRefuse be called before ResetNode? ResetNode(node); SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again")); /// \todo fix this !!! @@ -3201,6 +3308,11 @@ static void HandleConnect(SINT8 node) } } +/** Called when a PT_SERVERSHUTDOWN packet is received + * + * \param node The packet sender (should be the server) + * + */ static void HandleShutdown(SINT8 node) { (void)node; @@ -3210,6 +3322,11 @@ static void HandleShutdown(SINT8 node) M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING); } +/** Called when a PT_NODETIMEOUT packet is received + * + * \param node The packet sender (should be the server) + * + */ static void HandleTimeout(SINT8 node) { (void)node; @@ -3220,6 +3337,12 @@ static void HandleTimeout(SINT8 node) } #ifndef NONET +/** Called when a PT_SERVERINFO packet is received + * + * \param node The packet sender + * \note What happens if the packet comes from a client or something like that? + * + */ static void HandleServerInfo(SINT8 node) { // compute ping in ms @@ -3233,36 +3356,547 @@ static void HandleServerInfo(SINT8 node) } #endif -/** \brief GetPackets +/** Handles a packet received from a node that isn't in game + * + * \param node The packet sender + * \todo Choose a better name, as the packet can also come from the server apparently? + * \sa HandlePacketFromPlayer + * \sa GetPackets + * + */ +static void HandlePacketFromAwayNode(SINT8 node) +{ + if (node != servernode) + DEBFILE(va("Received packet from unknown host %d\n", node)); - \todo break this 300 line function into multiple functions -*/ -static void GetPackets(void) + 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 + /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? + /// Shouldn't them be downloaded even at intermission time? + /// Also, according to HandleConnect, the server will send the savegame even during intermission... + 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 + + } +} + +/** Handles a packet received from a node that is in game + * + * \param node The packet sender + * \todo Choose a better name + * \sa HandlePacketFromAwayNode + * \sa GetPackets + * + */ +static void HandlePacketFromPlayer(SINT8 node) {FILESTAMP XBOXSTATIC INT32 netconsole; - XBOXSTATIC SINT8 node; - XBOXSTATIC tic_t realend,realstart; + XBOXSTATIC tic_t realend, realstart; XBOXSTATIC UINT8 *pak, *txtpak, numtxtpak; FILESTAMP + txtpak = NULL; + + 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 + + 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 + // Use ExpandTics to 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 received 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_UnAcknowledgePacket(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_UnAcknowledgePacket(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 received 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); + } + + 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]; + } + + break; +#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 +} + +/** Handles all received packets, if any + * + * \todo Add details to this description (lol) + * + */ +static void GetPackets(void) +{FILESTAMP + XBOXSTATIC SINT8 node; // The packet sender +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) + && !server && cl_mode != CL_SEARCHING) { HandleShutdown(node); continue; } if (netbuffer->packettype == PT_NODETIMEOUT && node == servernode - && !server && cl_mode != cl_searching) + && !server && cl_mode != CL_SEARCHING) { HandleTimeout(node); continue; @@ -3279,481 +3913,13 @@ FILESTAMP 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); - } - - 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]; - } - - break; -#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 + // Packet received from someone already playing + if (nodeingame[node]) + HandlePacketFromPlayer(node); + // Packet received from someone trying to join + else + HandlePacketFromAwayNode(node); + } } // @@ -3768,6 +3934,10 @@ static INT16 Consistancy(void) { INT32 i; UINT32 ret = 0; +#ifdef MOBJCONSISTANCY + thinker_t *th; + mobj_t *mo; +#endif DEBFILE(va("TIC %u ", gametic)); @@ -3789,6 +3959,77 @@ static INT16 Consistancy(void) if (!G_PlatformGametype()) ret += P_GetRandSeed(); +#ifdef MOBJCONSISTANCY + if (!thinkercap.next) + return ret; + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + + mo = (mobj_t *)th; + + if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) + { + ret -= mo->type; + ret += mo->x; + ret -= mo->y; + ret += mo->z; + ret -= mo->momx; + ret += mo->momy; + ret -= mo->momz; + ret += mo->angle; + ret -= mo->flags; + ret += mo->flags2; + ret -= mo->eflags; + if (mo->target) + { + ret += mo->target->type; + ret -= mo->target->x; + ret += mo->target->y; + ret -= mo->target->z; + ret += mo->target->momx; + ret -= mo->target->momy; + ret += mo->target->momz; + ret -= mo->target->angle; + ret += mo->target->flags; + ret -= mo->target->flags2; + ret += mo->target->eflags; + ret -= mo->target->state - states; + ret += mo->target->tics; + ret -= mo->target->sprite; + ret += mo->target->frame; + } + else + ret ^= 0x3333; + if (mo->tracer && mo->tracer->type != MT_OVERLAY) + { + ret += mo->tracer->type; + ret -= mo->tracer->x; + ret += mo->tracer->y; + ret -= mo->tracer->z; + ret += mo->tracer->momx; + ret -= mo->tracer->momy; + ret += mo->tracer->momz; + ret -= mo->tracer->angle; + ret += mo->tracer->flags; + ret -= mo->tracer->flags2; + ret += mo->tracer->eflags; + ret -= mo->tracer->state - states; + ret += mo->tracer->tics; + ret -= mo->tracer->sprite; + ret += mo->tracer->frame; + } + else + ret ^= 0xAAAA; + ret -= mo->state - states; + ret += mo->tics; + ret -= mo->sprite; + ret += mo->frame; + } + } +#endif + return (INT16)(ret & 0xFFFF); } @@ -3829,7 +4070,7 @@ static void CL_SendClientCmd(void) HSendPacket(servernode, false, 0, packetsize); } - if (cl_mode == cl_connected || dedicated) + if (cl_mode == CL_CONNECTED || dedicated) { // send extra data if needed if (localtextcmd[0]) @@ -4212,12 +4453,12 @@ 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 + MasterClient_Ticker(); // Acking the Master Server if (!server) { if (!resynch_local_inprogress) - CL_SendClientCmd(); // send tic cmd + CL_SendClientCmd(); // Send tic cmd hu_resynching = resynch_local_inprogress; } else @@ -4243,21 +4484,21 @@ FILESTAMP counts = -666; } - // do not make tics while resynching + // 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 + 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 + for (; tictoclear < firstticstosend; tictoclear++) // Clear only when acknowledged + D_Clearticcmd(tictoclear); // Clear the maketic the new tic SV_SendTics(); - neededtic = maketic; // the server is a client too + neededtic = maketic; // The server is a client too } else hu_resynching = true; @@ -4271,7 +4512,7 @@ FILESTAMP M_Ticker(); CON_Ticker(); } - FiletxTicker(); + SV_FileSendTicker(); } /** Returns the number of players playing. diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 14b590926..fe80be1be 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -59,7 +59,7 @@ typedef enum // 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. + // 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. @@ -76,11 +76,16 @@ typedef enum NUMPACKETTYPE } packettype_t; +#ifdef PACKETDROP +void Command_Drop(void); +void Command_Droprate(void); +#endif + #if defined(_MSC_VER) #pragma pack(1) #endif -// client to server packet +// Client to server packet typedef struct { UINT8 client_tic; @@ -89,7 +94,7 @@ typedef struct ticcmd_t cmd; } ATTRPACK clientcmd_pak; -// splitscreen packet +// Splitscreen packet // WARNING: must have the same format of clientcmd_pak, for more easy use typedef struct { @@ -110,16 +115,16 @@ typedef struct UINT8 starttic; UINT8 numtics; UINT8 numslots; // "Slots filled": Highest player number in use plus one. - ticcmd_t cmds[45]; // normally [BACKUPTIC][MAXPLAYERS] but too large + ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large } ATTRPACK servertics_pak; -// sent to client when all consistency data +// Sent to client when all consistency data // for players has been restored typedef struct { UINT32 randomseed; - //ctf flag stuff + // CTF flag stuff SINT8 flagplayer[2]; INT32 flagloose[2]; INT32 flagflags[2]; @@ -127,11 +132,11 @@ typedef struct fixed_t flagy[2]; fixed_t flagz[2]; - UINT32 ingame; // spectator bit for each player - UINT32 ctfteam; // if not spectator, then which team? + 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. + UINT32 score[MAXPLAYERS]; // Everyone's score INT16 numboxes[MAXPLAYERS]; INT16 totalring[MAXPLAYERS]; tic_t realtime[MAXPLAYERS]; @@ -140,14 +145,14 @@ typedef struct typedef struct { - //player stuff + // 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 + UINT8 playerstate; // playerstate_t + UINT32 pflags; // pflags_t + UINT8 panim; // panim_t angle_t aiming; INT32 currentweapon; @@ -174,9 +179,9 @@ typedef struct UINT8 charability; UINT8 charability2; UINT32 charflags; - UINT32 thokitem; //mobjtype_t - UINT32 spinitem; //mobjtype_t - UINT32 revitem; //mobjtype_t + UINT32 thokitem; // mobjtype_t + UINT32 spinitem; // mobjtype_t + UINT32 revitem; // mobjtype_t fixed_t actionspd; fixed_t mindash; fixed_t maxdash; @@ -230,7 +235,7 @@ typedef struct INT32 onconveyor; //player->mo stuff - UINT8 hasmo; //boolean + UINT8 hasmo; // Boolean angle_t angle; fixed_t x; @@ -257,10 +262,10 @@ typedef struct typedef struct { - UINT8 version; // different versions don't work - UINT8 subversion; // contains build version + UINT8 version; // Different versions don't work + UINT8 subversion; // Contains build version - // server launch stuffs + // Server launch stuffs UINT8 serverplayer; UINT8 totalslotnum; // "Slots": highest player number in use plus one. @@ -274,18 +279,18 @@ typedef struct UINT8 gametype; UINT8 modifiedgame; - SINT8 adminplayer; // needs to be signed + SINT8 adminplayer; // Needs to be signed - char server_context[8]; // unique context id, generated at server startup. + char server_context[8]; // Unique context id, generated at server startup. - UINT8 varlengthinputs[0]; // playernames and netvars + UINT8 varlengthinputs[0]; // Playernames and netvars } ATTRPACK serverconfig_pak; typedef struct { UINT8 fileid; UINT32 position; UINT16 size; - UINT8 data[0]; // size is variable using hardware_MAXPACKETLENGTH + UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH } ATTRPACK filetx_pak; #ifdef _MSC_VER @@ -294,14 +299,14 @@ typedef struct { typedef struct { - UINT8 version; // different versions don't work - UINT8 subversion; // contains build version + UINT8 version; // Different versions don't work + UINT8 subversion; // Contains build version UINT8 localplayers; UINT8 mode; } ATTRPACK clientconfig_pak; #define MAXSERVERNAME 32 -// this packet is too large +// This packet is too large typedef struct { UINT8 version; @@ -367,45 +372,45 @@ typedef struct } ATTRPACK plrconfig; // -// Network packet data. +// Network packet data // typedef struct { UINT32 checksum; - UINT8 ack; // if not null the node asks for acknowledgement, the receiver must resend the ack - UINT8 ackreturn; // the return of the ack number + UINT8 ack; // If not zero the node asks for acknowledgement, the receiver must resend the ack + UINT8 ackreturn; // The return of the ack number UINT8 packettype; - UINT8 reserved; // padding + UINT8 reserved; // Padding union { - clientcmd_pak clientpak; // 144 bytes - 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 - serverinfo_pak serverinfo; // 1024 bytes - serverrefuse_pak serverrefuse; // 65025 bytes - askinfo_pak askinfo; // 61 bytes - msaskinfo_pak msaskinfo; // 22 bytes - plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes - plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes + clientcmd_pak clientpak; // 144 bytes + client2cmd_pak client2pak; // 200 bytes + servertics_pak serverpak; // 132495 bytes (more around 360, no?) + serverconfig_pak servercfg; // 773 bytes + resynchend_pak resynchend; // + resynch_pak resynchpak; // + UINT8 resynchgot; // + UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...) + filetx_pak filetxpak; // 139 bytes + clientconfig_pak clientcfg; // 136 bytes + serverinfo_pak serverinfo; // 1024 bytes + serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...) + askinfo_pak askinfo; // 61 bytes + msaskinfo_pak msaskinfo; // 22 bytes + plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes (I'd say 36~38) + plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes (welp they ARE) #ifdef NEWPING - UINT32 pingtable[MAXPLAYERS]; // 128 bytes + UINT32 pingtable[MAXPLAYERS]; // 128 bytes #endif - } u; // this is needed to pack diff packet types data together + } u; // This is needed to pack diff packet types data together } ATTRPACK doomdata_t; #if defined(_MSC_VER) #pragma pack() #endif -#define MAXSERVERLIST 64 // depends only on the display +#define MAXSERVERLIST 64 // Depends only on the display typedef struct { SINT8 node; @@ -416,7 +421,7 @@ extern serverelem_t serverlist[MAXSERVERLIST]; extern UINT32 serverlistcount; extern INT32 mapchangepending; -// points inside doomcom +// Points inside doomcom extern doomdata_t *netbuffer; extern consvar_t cv_playbackspeed; @@ -437,7 +442,7 @@ extern consvar_t cv_playbackspeed; #define KICK_MSG_CUSTOM_BAN 8 extern boolean server; -extern boolean dedicated; // for dedicated server +extern boolean dedicated; // For dedicated server extern UINT16 software_MAXPACKETLENGTH; extern boolean acceptnewnode; extern SINT8 servernode; @@ -452,11 +457,11 @@ extern UINT32 playerpingtable[MAXPLAYERS]; extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend; -// used in d_net, the only dependence +// Used in d_net, the only dependence tic_t ExpandTics(INT32 low); void D_ClientServerInit(void); -// initialise the other field +// Initialise the other field void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum)); void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam); void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player @@ -474,14 +479,14 @@ void CL_RemoveSplitscreenPlayer(void); void CL_Reset(void); void CL_ClearPlayer(INT32 playernum); void CL_UpdateServerList(boolean internetsearch, INT32 room); -// is there a game running +// Is there a game running boolean Playing(void); // Broadcasts special packets to other players // to notify of game exit void D_QuitNetGame(void); -//? how many ticks to run? +//? How many ticks to run? void TryRunTics(tic_t realtic); // extra data for lmps diff --git a/src/d_main.c b/src/d_main.c index b61ec4143..2caf50087 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -73,6 +73,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #include "dehacked.h" // Dehacked list test #include "m_cond.h" // condition initialization #include "fastcmp.h" +#include "keys.h" #ifdef CMAKECONFIG #include "config.h" @@ -176,6 +177,38 @@ void D_PostEvent(const event_t *ev) void D_PostEvent_end(void) {}; #endif +// modifier keys +UINT8 shiftdown = 0; // 0x1 left, 0x2 right +UINT8 ctrldown = 0; // 0x1 left, 0x2 right +UINT8 altdown = 0; // 0x1 left, 0x2 right +// +// D_ModifierKeyResponder +// Sets global shift/ctrl/alt variables, never actually eats events +// +static inline void D_ModifierKeyResponder(event_t *ev) +{ + if (ev->type == ev_keydown) switch (ev->data1) + { + case KEY_LSHIFT: shiftdown |= 0x1; return; + case KEY_RSHIFT: shiftdown |= 0x2; return; + case KEY_LCTRL: ctrldown |= 0x1; return; + case KEY_RCTRL: ctrldown |= 0x2; return; + case KEY_LALT: altdown |= 0x1; return; + case KEY_RALT: altdown |= 0x2; return; + default: return; + } + else if (ev->type == ev_keyup) switch (ev->data1) + { + case KEY_LSHIFT: shiftdown &= ~0x1; return; + case KEY_RSHIFT: shiftdown &= ~0x2; return; + case KEY_LCTRL: ctrldown &= ~0x1; return; + case KEY_RCTRL: ctrldown &= ~0x2; return; + case KEY_LALT: altdown &= ~0x1; return; + case KEY_RALT: altdown &= ~0x2; return; + default: return; + } +} + // // D_ProcessEvents // Send all the events of the given timestamp down the responder chain @@ -188,6 +221,9 @@ void D_ProcessEvents(void) { ev = &events[eventtail]; + // Set global shift/ctrl/alt down variables + D_ModifierKeyResponder(ev); // never eats events + // Screenshots over everything so that they can be taken anywhere. if (M_ScreenshotResponder(ev)) continue; // ate the event diff --git a/src/d_net.c b/src/d_net.c index 03e126b50..6be1dbe5c 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -31,15 +31,15 @@ // // NETWORKING // -// gametic is the tic about to be (or currently being) run -// server: +// gametic is the tic about to (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 +// nettics is the tic for each node +// firstticstosend is the lowest value of nettics +// Client: +// neededtic is the tic needed by the client to run the game +// firstticstosend is used to optimize a condition +// Normally maketic >= gametic > 0 #define FORCECLOSE 0x8000 tic_t connectiontimeout = (15*TICRATE); @@ -129,9 +129,9 @@ boolean Net_GetNetStat(void) // ----------------------------------------------------------------- // Some structs and functions for acknowledgement of packets // ----------------------------------------------------------------- -#define MAXACKPACKETS 96 // minimum number of nodes +#define MAXACKPACKETS 96 // Minimum number of nodes (wat) #define MAXACKTOSEND 96 -#define URGENTFREESLOTENUM 10 +#define URGENTFREESLOTNUM 10 #define ACKTOSENDTIMEOUT (TICRATE/11) #ifndef NONET @@ -139,10 +139,10 @@ typedef struct { UINT8 acknum; UINT8 nextacknum; - UINT8 destinationnode; - tic_t senttime; - UINT16 length; - UINT16 resentnum; + UINT8 destinationnode; // The node to send the ack to + tic_t senttime; // The time when the ack was sent + UINT16 length; // The packet size + UINT16 resentnum; // The number of union { SINT8 raw[MAXPACKETLENGTH]; doomdata_t data; @@ -212,11 +212,16 @@ FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b) return d; } -// return a free acknum and copy netbuffer in the ackpak table +/** Sets freeack to a free acknum and copies the netbuffer in the ackpak table + * + * \param freeack The address to store the free acknum at + * \param lowtimer ??? + * \return True if a free acknum was found + */ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) { node_t *node = &nodes[doomcom->remotenode]; - INT32 i, numfreeslote = 0; + INT32 i, numfreeslot = 0; if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0) { @@ -227,10 +232,13 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) 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; + // For low priority packets, make sure to let freeslots so urgent packets can be sent + if (netbuffer->packettype >= PT_CANFAIL) + { + numfreeslot++; + if (numfreeslot <= URGENTFREESLOTNUM) + continue; + } ackpak[i].acknum = node->nextacknum; ackpak[i].nextacknum = node->nextacknum; @@ -241,7 +249,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) ackpak[i].length = doomcom->datalength; if (lowtimer) { - // lowtime mean can't be sent now so try it soon as possible + // Lowtime means can't be sent now so try it as soon as possible ackpak[i].senttime = 0; ackpak[i].resentnum = 1; } @@ -254,7 +262,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) *freeack = ackpak[i].acknum; - sendackpacket++; // for stat + sendackpacket++; // For stat return true; } @@ -266,14 +274,14 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer) return false; } -// Get a ack to send in the queu of this node +// Get a ack to send in the queue of this node static UINT8 GetAcktosend(INT32 node) { nodes[node].lasttimeacktosend_sent = I_GetTime(); return nodes[node].firstacktosend; } -static void Removeack(INT32 i) +static void RemoveAck(INT32 i) { INT32 node = ackpak[i].destinationnode; #ifndef NEWPING @@ -294,27 +302,27 @@ static void Removeack(INT32 i) Net_CloseConnection(node); } -// we have got a packet proceed the ack request and ack return +// 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 + // 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 + // 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); + RemoveAck(i); } } - // received a packet with ack, queue it to send the ack back + // Received a packet with ack, queue it to send the ack back if (netbuffer->ack) { UINT8 ack = netbuffer->ack; @@ -323,23 +331,23 @@ static boolean Processackpak(void) { DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack)); duppacket++; - goodpacket = false; // discard packet (duplicate) + goodpacket = false; // Discard packet (duplicate) } else { - // check if it is not already in the queue + // 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) + 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 + // 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; @@ -383,10 +391,10 @@ static boolean Processackpak(void) } } } - else // out of order packet + else // Out of order packet { - // don't increment firsacktosend, put it in asktosend queue - // will be incremented when the nextfirstack comes (code above) + // 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) @@ -394,8 +402,8 @@ static boolean Processackpak(void) 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 :( + 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; } @@ -430,25 +438,24 @@ static void GotAcks(void) 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 - } + RemoveAck(i); + // 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 + else 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 + // 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; @@ -456,12 +463,12 @@ static inline void Net_ConnectionTimeout(INT32 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 + // 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 +// Resend the data if needed void Net_AckTicker(void) { #ifndef NONET @@ -497,7 +504,7 @@ void Net_AckTicker(void) ackpak[i].senttime = I_GetTime(); ackpak[i].resentnum++; ackpak[i].nextacknum = node->nextacknum; - retransmit++; // for stat + retransmit++; // For stat HSendPacket((INT32)(node - nodes), false, ackpak[i].acknum, (size_t)(ackpak[i].length - BASEPACKETSIZE)); } @@ -505,11 +512,11 @@ void Net_AckTicker(void) for (i = 1; i < MAXNETNODES; i++) { - // this is something like node open flag + // 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 + // 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); @@ -523,9 +530,9 @@ void Net_AckTicker(void) #endif } -// remove last packet received ack before resending the ackret +// Remove last packet received ack before resending the ackreturn // (the higher layer doesn't have room, or something else ....) -void Net_UnAcknowledgPacket(INT32 node) +void Net_UnAcknowledgePacket(INT32 node) { #ifdef NONET (void)node; @@ -564,20 +571,29 @@ void Net_UnAcknowledgPacket(INT32 node) #endif } -boolean Net_AllAckReceived(void) -{ #ifndef NONET +/** Checks if all acks have been received + * + * \return True if all acks have been received + * + */ +static boolean Net_AllAcksReceived(void) +{ INT32 i; for (i = 0; i < MAXACKPACKETS; i++) if (ackpak[i].acknum) return false; -#endif return true; } +#endif -// wait for all ackreturns with timeout in seconds +/** Waits for all ackreturns + * + * \param timeout Timeout in seconds + * + */ void Net_WaitAllAckReceived(UINT32 timeout) { #ifdef NONET @@ -587,7 +603,7 @@ void Net_WaitAllAckReceived(UINT32 timeout) timeout = tictac + timeout*NEWTICRATE; HGetPacket(); - while (timeout > I_GetTime() && !Net_AllAckReceived()) + while (timeout > I_GetTime() && !Net_AllAcksReceived()) { while (tictac == I_GetTime()) I_Sleep(); @@ -598,18 +614,18 @@ void Net_WaitAllAckReceived(UINT32 timeout) #endif } -static void InitNode(INT32 node) +static void InitNode(node_t *node) { - nodes[node].acktosend_head = nodes[node].acktosend_tail = 0; + node->acktosend_head = node->acktosend_tail = 0; #ifndef NEWPING - nodes[node].ping = PINGDEFAULT; - nodes[node].varping = VARPINGDEFAULT; - nodes[node].timeout = TIMEOUT(nodes[node].ping,nodes[node].varping); + node->ping = PINGDEFAULT; + node->varping = VARPINGDEFAULT; + node->timeout = TIMEOUT(node->ping, node->varping); #endif - nodes[node].firstacktosend = 0; - nodes[node].nextacknum = 1; - nodes[node].remotefirstack = 0; - nodes[node].flags = 0; + node->firstacktosend = 0; + node->nextacknum = 1; + node->remotefirstack = 0; + node->flags = 0; } static void InitAck(void) @@ -622,9 +638,14 @@ static void InitAck(void) #endif for (i = 0; i < MAXNETNODES; i++) - InitNode(i); + InitNode(&nodes[i]); } +/** Removes all acks of a given packet type + * + * \param packettype The packet type to forget + * + */ void Net_AbortPacketType(UINT8 packettype) { #ifdef NONET @@ -676,8 +697,8 @@ void Net_CloseConnection(INT32 node) ackpak[i].acknum = 0; } - InitNode(node); - AbortSendFiles(node); + InitNode(&nodes[node]); + SV_AbortSendFiles(node); I_NetFreeNodenum(node); #endif } @@ -729,9 +750,15 @@ static void fprintfstring(char *s, size_t len) } if (mode) fprintf(debugfile, "]"); +} + +static void fprintfstringnewline(char *s, size_t len) +{ + fprintfstring(s, len); fprintf(debugfile, "\n"); } +/// \warning Keep this up-to-date if you add/remove/rename packet types static const char *packettypename[NUMPACKETTYPE] = { "NOTHING", @@ -749,15 +776,22 @@ static const char *packettypename[NUMPACKETTYPE] = "ASKINFO", "SERVERINFO", + "PLAYERINFO", "REQUESTFILE", "ASKINFOVIAMS", - "PLAYERCONFIGS", + "RESYNCHEND", + "RESYNCHGET", + "FILEFRAGMENT", "TEXTCMD", "TEXTCMD2", "CLIENTJOIN", "NODETIMEOUT", + "RESYNCHING", +#ifdef NEWPING + "PING" +#endif }; static void DebugPrintpacket(const char *header) @@ -770,20 +804,29 @@ static void DebugPrintpacket(const char *header) { case PT_ASKINFO: case PT_ASKINFOVIAMS: - fprintf(debugfile, " time %u\n", (tic_t)LONG(netbuffer->u.askinfo.time) ); + 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: + { + servertics_pak *serverpak = &netbuffer->u.serverpak; + ticcmd_t *cmd = &serverpak->cmds[serverpak->numslots * serverpak->numtics]; + size_t ntxtcmd = &((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)cmd; + 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])); + (UINT32)ExpandTics(serverpak->starttic), serverpak->numslots, serverpak->numtics, sizeu1(ntxtcmd)); + fprintfstring((char *)cmd, 3); + if (ntxtcmd > 4) + { + fprintf(debugfile, "[%s]", netxcmdnames[*(((UINT8 *)cmd) + 3) - 1]); + fprintfstring(((char *)cmd) + 4, ntxtcmd - 4); + } + fprintf(debugfile, "\n"); break; + } case PT_CLIENTCMD: case PT_CLIENT2CMD: case PT_CLIENTMIS: @@ -797,7 +840,8 @@ static void DebugPrintpacket(const char *header) 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]); + fprintf(debugfile, "[%s]", netxcmdnames[netbuffer->u.textcmd[1] - 1]); + fprintfstringnewline((char *)netbuffer->u.textcmd + 2, netbuffer->u.textcmd[0] - 1); break; case PT_SERVERCFG: fprintf(debugfile, " playerslots %d clientnode %d serverplayer %d " @@ -813,7 +857,7 @@ static void DebugPrintpacket(const char *header) netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.mapname, netbuffer->u.serverinfo.fileneedednum, (UINT32)LONG(netbuffer->u.serverinfo.time)); - fprintfstring((char *)netbuffer->u.serverinfo.fileneeded, + fprintfstringnewline((char *)netbuffer->u.serverinfo.fileneeded, (UINT8)((UINT8 *)netbuffer + doomcom->datalength - (UINT8 *)netbuffer->u.serverinfo.fileneeded)); break; @@ -827,20 +871,100 @@ static void DebugPrintpacket(const char *header) break; case PT_REQUESTFILE: default: // write as a raw packet - fprintfstring((char *)netbuffer->u.textcmd, + fprintfstringnewline((char *)netbuffer->u.textcmd, (UINT8)((UINT8 *)netbuffer + doomcom->datalength - (UINT8 *)netbuffer->u.textcmd)); break; } } #endif +#ifdef PACKETDROP +static INT32 packetdropquantity[NUMPACKETTYPE] = {0}; +static INT32 packetdroprate = 0; + +void Command_Drop(void) +{ + INT32 packetquantity; + const char *packetname; + size_t i; + + if (COM_Argc() < 2) + { + CONS_Printf("drop [quantity]: drop packets\n" + "drop reset: cancel all packet drops"); + return; + } + + if (!(stricmp(COM_Argv(1), "reset") && stricmp(COM_Argv(1), "cancel") && stricmp(COM_Argv(1), "stop"))) + { + memset(packetdropquantity, 0, sizeof(packetdropquantity)); + return; + } + + if (COM_Argc() >= 3) + { + packetquantity = atoi(COM_Argv(2)); + if (packetquantity <= 0 && COM_Argv(2)[0] != '0') + { + CONS_Printf("Invalid quantity\n"); + return; + } + } + else + packetquantity = -1; + + packetname = COM_Argv(1); + + if (!(stricmp(packetname, "all") && stricmp(packetname, "any"))) + for (i = 0; i < NUMPACKETTYPE; i++) + packetdropquantity[i] = packetquantity; + else + { + for (i = 0; i < NUMPACKETTYPE; i++) + if (!stricmp(packetname, packettypename[i])) + { + packetdropquantity[i] = packetquantity; + return; + } + + CONS_Printf("Unknown packet name\n"); + } +} + +void Command_Droprate(void) +{ + INT32 droprate; + + if (COM_Argc() < 2) + { + CONS_Printf("Packet drop rate: %d%%\n", packetdroprate); + return; + } + + droprate = atoi(COM_Argv(1)); + if ((droprate <= 0 && COM_Argv(1)[0] != '0') || droprate > 100) + { + CONS_Printf("Packet drop rate must be between 0 and 100!\n"); + return; + } + + packetdroprate = droprate; +} + +static boolean ShouldDropPacket(void) +{ + return (packetdropquantity[netbuffer->packettype]) + || (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100; +} +#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 (node == 0) // Packet is to go back to us { if ((rebound_head+1) % MAXREBOUND == rebound_tail) { @@ -871,7 +995,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen (void)reliable; (void)acknum; #else - // do this before GetFreeAcknum because this function backup + // do this before GetFreeAcknum because this function backups // the current packet doomcom->remotenode = (INT16)node; if (doomcom->datalength <= 0) @@ -884,7 +1008,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen return false; } - if (node < MAXNETNODES) // can be a broadcast + if (node < MAXNETNODES) // Can be a broadcast netbuffer->ackreturn = GetAcktosend(node); else netbuffer->ackreturn = 0; @@ -905,20 +1029,30 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen netbuffer->ack = acknum; netbuffer->checksum = NetbufferChecksum(); - sendbytes += packetheaderlength + doomcom->datalength; // for stat + sendbytes += packetheaderlength + doomcom->datalength; // For stat - // simulate internet :) - if (true || rand()<(INT32)RAND_MAX/5) +#ifdef PACKETDROP + // Simulate internet :) + //if (rand() >= (INT32)(RAND_MAX * (PACKETLOSSRATE / 100.f))) + if (!ShouldDropPacket()) { +#endif #ifdef DEBUGFILE if (debugfile) - DebugPrintpacket("SEND"); + DebugPrintpacket("SENT"); #endif I_NetSend(); +#ifdef PACKETDROP } + else + { + if (packetdropquantity[netbuffer->packettype] > 0) + packetdropquantity[netbuffer->packettype]--; #ifdef DEBUGFILE - else if (debugfile) - DebugPrintpacket("NOTSEND"); + if (debugfile) + DebugPrintpacket("NOT SENT"); +#endif + } #endif #endif // ndef NONET @@ -933,7 +1067,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen // boolean HGetPacket(void) { - // get a packet from self + // Get a packet from self if (rebound_tail != rebound_head) { M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]); @@ -963,11 +1097,11 @@ boolean HGetPacket(void) if (doomcom->remotenode == -1) return false; - getbytes += packetheaderlength + doomcom->datalength; // for stat + getbytes += packetheaderlength + doomcom->datalength; // For stat if (doomcom->remotenode >= MAXNETNODES) { - DEBFILE(va("receive packet from node %d !\n", doomcom->remotenode)); + DEBFILE(va("Received packet from node %d!\n", doomcom->remotenode)); continue; } @@ -1230,4 +1364,6 @@ void D_CloseConnection(void) netgame = false; addedtogame = false; } + + D_ResetTiccmds(); } diff --git a/src/d_net.h b/src/d_net.h index 285b44235..190e07a60 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -18,10 +18,10 @@ #ifndef __D_NET__ #define __D_NET__ -// Max computers in a game. +// Max computers in a game #define MAXNETNODES 32 #define BROADCASTADDR MAXNETNODES -#define MAXSPLITSCREENPLAYERS 2 // max number of players on a single computer +#define MAXSPLITSCREENPLAYERS 2 // Max number of players on a single computer #define STATLENGTH (TICRATE*2) @@ -32,17 +32,16 @@ extern float lostpercent, duppercent, gamelostpercent; extern INT32 packetheaderlength; boolean Net_GetNetStat(void); extern INT32 getbytes; -extern INT64 sendbytes; // realtime updated +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 +extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen) +extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen +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 +// If reliable return true if packet sent, 0 else boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength); boolean HGetPacket(void); @@ -52,9 +51,10 @@ void D_SaveBan(void); #endif boolean D_CheckNetGame(void); void D_CloseConnection(void); -void Net_UnAcknowledgPacket(INT32 node); +void Net_UnAcknowledgePacket(INT32 node); void Net_CloseConnection(INT32 node); void Net_AbortPacketType(UINT8 packettype); void Net_SendAcks(INT32 node); void Net_WaitAllAckReceived(UINT32 timeout); + #endif diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4f73a2564..d70805e1c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -365,6 +365,35 @@ boolean splitscreen = false; boolean circuitmap = false; INT32 adminplayer = -1; +/// \warning Keep this up-to-date if you add/remove/rename net text commands +const char *netxcmdnames[MAXNETXCMD - 1] = +{ + "NAMEANDCOLOR", + "WEAPONPREF", + "KICK", + "NETVAR", + "SAY", + "MAP", + "EXITLEVEL", + "ADDFILE", + "PAUSE", + "ADDPLAYER", + "TEAMCHANGE", + "CLEARSCORES", + "LOGIN", + "VERIFIED", + "RANDOMSEED", + "RUNSOC", + "REQADDFILE", + "DELFILE", + "SETMOTD", + "SUICIDE", +#ifdef HAVE_BLUA + "LUACMD", + "LUAVAR" +#endif +}; + // ========================================================================= // SERVER STARTUP // ========================================================================= diff --git a/src/d_netcmd.h b/src/d_netcmd.h index c090699f1..d8fae72f7 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -162,6 +162,8 @@ typedef enum MAXNETXCMD } netxcmd_t; +extern const char *netxcmdnames[MAXNETXCMD - 1]; + #if defined(_MSC_VER) #pragma pack(1) #endif diff --git a/src/d_netfil.c b/src/d_netfil.c index 85196217f..4e3d70fa9 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -62,34 +62,37 @@ #include -static void SendFile(INT32 node, const char *filename, UINT8 fileid); +static void SV_SendFile(INT32 node, const char *filename, UINT8 fileid); -// sender structure +// Sender structure typedef struct filetx_s { INT32 ram; - char *filename; // name of the file or ptr of the data in ram - UINT32 size; + union { + char *filename; // Name of the file + char *ram; // Pointer to the data in RAM + } id; + UINT32 size; // Size of the file UINT8 fileid; - INT32 node; // destination - struct filetx_s *next; // a queue + INT32 node; // Destination + struct filetx_s *next; // Next file in the list } filetx_t; -// current transfers (one for each node) +// Current transfers (one for each node) typedef struct filetran_s { - filetx_t *txlist; - UINT32 position; - FILE *currentfile; + filetx_t *txlist; // Linked list of all files for the node + UINT32 position; // The current position in the file + FILE *currentfile; // The file currently being sent/received } filetran_t; static filetran_t transfer[MAXNETNODES]; -// read time of file: stat _stmtime -// write time of file: utime +// Read time of file: stat _stmtime +// Write time of file: utime -// receiver structure -INT32 fileneedednum; -fileneeded_t fileneeded[MAX_WADFILES]; +// Receiver structure +INT32 fileneedednum; // Number of files needed to join the server +fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files char downloaddir[256] = "DOWNLOAD"; #ifdef CLIENT_LOADINGSCREEN @@ -100,6 +103,7 @@ INT32 lastfilenum = 0; /** Fills a serverinfo packet with information about wad files loaded. * * \todo Give this function a better name since it is in global scope. + * */ UINT8 *PutFileNeeded(void) { @@ -111,19 +115,19 @@ UINT8 *PutFileNeeded(void) for (i = 0; i < numwadfiles; i++) { - // if it has only music/sound lumps, mark it as unimportant + // If it has only music/sound lumps, mark it as unimportant if (W_VerifyNMUSlumps(wadfiles[i]->filename)) filestatus = 0; else - filestatus = 1; // important + filestatus = 1; // Important // Store in the upper four bits if (!cv_downloading.value) - filestatus += (2 << 4); // won't send + filestatus += (2 << 4); // Won't send else if ((wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024)) - filestatus += (0 << 4); // won't send + filestatus += (0 << 4); // Won't send else - filestatus += (1 << 4); // will send if requested + filestatus += (1 << 4); // Will send if requested bytesused += (nameonlylength(wadfilename) + 22); @@ -144,7 +148,12 @@ UINT8 *PutFileNeeded(void) return p; } -// parse the serverinfo packet and fill fileneeded table on client +/** Parses the serverinfo packet and fills the fileneeded table on client + * + * \param fileneedednum_parm The number of files needed to join the server + * \param fileneededstr The memory block containing the list of needed files + * + */ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr) { INT32 i; @@ -155,14 +164,14 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr) p = (UINT8 *)fileneededstr; for (i = 0; i < fileneedednum; i++) { - fileneeded[i].status = FS_NOTFOUND; - filestatus = READUINT8(p); + fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet + filestatus = READUINT8(p); // The first byte is the file status fileneeded[i].important = (UINT8)(filestatus & 3); fileneeded[i].willsend = (UINT8)(filestatus >> 4); - fileneeded[i].totalsize = READUINT32(p); - fileneeded[i].phandle = NULL; - READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); - READMEM(p, fileneeded[i].md5sum, 16); + fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size + fileneeded[i].file = NULL; // The file isn't open yet + READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name + READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum } } @@ -171,13 +180,16 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave) fileneedednum = 1; fileneeded[0].status = FS_REQUESTED; fileneeded[0].totalsize = UINT32_MAX; - fileneeded[0].phandle = NULL; + fileneeded[0].file = NULL; memset(fileneeded[0].md5sum, 0, 16); strcpy(fileneeded[0].filename, tmpsave); } /** Checks the server to see if we CAN download all the files, * before starting to create them and requesting. + * + * \return True if we can download all the files + * */ boolean CL_CheckDownloadable(void) { @@ -239,8 +251,12 @@ boolean CL_CheckDownloadable(void) return false; } -/** Send requests for files in the ::fileneeded table with a status of +/** Sends requests for files in the ::fileneeded table with a status of * ::FS_NOTFOUND. + * + * \return True if the packet was successfully sent + * \note Sends a PT_REQUESTFILE packet + * */ boolean CL_SendRequestFile(void) { @@ -298,11 +314,17 @@ void Got_RequestFilePak(INT32 node) if (id == 0xFF) break; READSTRINGN(p, wad, MAX_WADPATH); - SendFile(node, wad, id); + SV_SendFile(node, wad, id); } } -// client check if the fileneeded aren't already loaded or on the disk +/** Checks if the files needed aren't already loaded or on the disk + * + * \return 0 if some files are missing + * 1 if all files exist + * 2 if some already loaded files are not requested or are in a different order + * + */ INT32 CL_CheckFiles(void) { INT32 i, j; @@ -333,7 +355,7 @@ INT32 CL_CheckFiles(void) } if (j < numwadfiles && W_VerifyNMUSlumps(wadfiles[j]->filename)) { - // unimportant on our side. still don't care. + // Unimportant on our side. still don't care. ++j; continue; } @@ -343,11 +365,11 @@ INT32 CL_CheckFiles(void) if (i >= fileneedednum || j >= numwadfiles) return 2; - // for the sake of speed, only bother with a md5 check + // For the sake of speed, only bother with a md5 check if (memcmp(wadfiles[j]->md5sum, fileneeded[i].md5sum, 16)) return 2; - // it's accounted for! let's keep going. + // It's accounted for! let's keep going. CONS_Debug(DBG_NETPLAY, "'%s' accounted for\n", fileneeded[i].filename); fileneeded[i].status = FS_OPEN; ++i; @@ -360,7 +382,7 @@ INT32 CL_CheckFiles(void) { CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename); - // check in allready loaded files + // Check in already loaded files for (j = 1; wadfiles[j]; j++) { nameonly(strcpy(wadfilename, wadfiles[j]->filename)); @@ -383,7 +405,7 @@ INT32 CL_CheckFiles(void) return ret; } -// load it now +// Load it now void CL_LoadServerFiles(void) { INT32 i; @@ -394,7 +416,7 @@ void CL_LoadServerFiles(void) for (i = 1; i < fileneedednum; i++) { if (fileneeded[i].status == FS_OPEN) - continue; // already loaded + continue; // Already loaded else if (fileneeded[i].status == FS_FOUND) { P_AddWadFile(fileneeded[i].filename, NULL); @@ -423,133 +445,200 @@ void CL_LoadServerFiles(void) DEBFILE(va("File %s found but with different md5sum\n", fileneeded[i].filename)); } else if (fileneeded[i].important) - I_Error("Try to load file %s with status of %d\n", fileneeded[i].filename, - fileneeded[i].status); + { + const char *s; + switch(fileneeded[i].status) + { + case FS_NOTFOUND: + s = "FS_NOTFOUND"; + break; + case FS_REQUESTED: + s = "FS_REQUESTED"; + break; + case FS_DOWNLOADING: + s = "FS_DOWNLOADING"; + break; + default: + s = "unknown"; + break; + } + I_Error("Try to load file \"%s\" with status of %d (%s)\n", fileneeded[i].filename, + fileneeded[i].status, s); + } } } -// little optimization to test if there is a file in the queue -static INT32 filetosend = 0; +// Number of files to send +// Little optimization to quickly test if there is a file in the queue +static INT32 filestosend = 0; -static void SendFile(INT32 node, const char *filename, UINT8 fileid) +/** Adds a file to the file list for a node + * + * \param node The node to send the file to + * \param filename The file to send + * \param fileid ??? + * \sa SV_SendRam + * + */ +static void SV_SendFile(INT32 node, const char *filename, UINT8 fileid) { - filetx_t **q; - filetx_t *p; + filetx_t **q; // A pointer to the "next" field of the last file in the list + filetx_t *p; // The new file request INT32 i; char wadfilename[MAX_WADPATH]; + // Find the last file in the list and set a pointer to its "next" field q = &transfer[node].txlist; while (*q) q = &((*q)->next); + + // Allocate a file request and append it to the file list 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"); + if (!p) + I_Error("SV_SendFile: No more memory\n"); - // a minimum of security, can get only file in srb2 direcory - strlcpy(p->filename, filename, MAX_WADPATH); - nameonly(p->filename); + // Initialise with zeros + memset(p, 0, sizeof (filetx_t)); - // check first in wads loaded the majority of case + // Allocate the file name + p->id.filename = (char *)malloc(MAX_WADPATH); + if (!p->id.filename) + I_Error("SV_SendFile: No more memory\n"); + + // Set the file name and get rid of the path + strlcpy(p->id.filename, filename, MAX_WADPATH); + nameonly(p->id.filename); + + // Look for the requested file through all loaded files for (i = 0; wadfiles[i]; i++) { strlcpy(wadfilename, wadfiles[i]->filename, MAX_WADPATH); nameonly(wadfilename); - if (!stricmp(wadfilename, p->filename)) + if (!stricmp(wadfilename, p->id.filename)) { - // copy filename with full path - strlcpy(p->filename, wadfiles[i]->filename, MAX_WADPATH); + // Copy file name with full path + strlcpy(p->id.filename, wadfiles[i]->filename, MAX_WADPATH); break; } } + // Handle non-loaded file requests if (!wadfiles[i]) { DEBFILE(va("%s not found in wadfiles\n", filename)); - // this formerly checked if (!findfile(p->filename, NULL, true)) + // This formerly checked if (!findfile(p->id.filename, NULL, true)) - // not found - // don't inform client (probably hacker) + // Not found + // Don't inform client (probably someone who thought they could leak 2.2 ACZ) DEBFILE(va("Client %d request %s: not found\n", node, filename)); - free(p->filename); + free(p->id.filename); free(p); *q = NULL; return; } + // Handle huge file requests (i.e. bigger than cv_maxsend.value KB) if (wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024) { - // too big - // don't inform client (client sucks, man) + // 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->id.filename); free(p); *q = NULL; return; } DEBFILE(va("Sending file %s (id=%d) to %d\n", filename, fileid, node)); - p->ram = SF_FILE; + p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it p->fileid = fileid; - p->next = NULL; // end of list - filetosend++; + p->next = NULL; // End of list + filestosend++; } -void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid) +/** Adds a memory block to the file list for a node + * + * \param node The node to send the memory block to + * \param data The memory block to send + * \param size The size of the block in bytes + * \param freemethod How to free the block after it has been sent + * \param fileid ??? + * \sa SV_SendFile + * + */ +void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid) { - filetx_t **q; - filetx_t *p; + filetx_t **q; // A pointer to the "next" field of the last file in the list + filetx_t *p; // The new file request + // Find the last file in the list and set a pointer to its "next" field q = &transfer[node].txlist; while (*q) q = &((*q)->next); + + // Allocate a file request and append it to the file list p = *q = (filetx_t *)malloc(sizeof (filetx_t)); - if (p) - memset(p, 0, sizeof (filetx_t)); - else - I_Error("SendRam: No more ram\n"); - p->ram = freemethod; - p->filename = data; + if (!p) + I_Error("SV_SendRam: No more memory\n"); + + // Initialise with zeros + memset(p, 0, sizeof (filetx_t)); + + p->ram = freemethod; // Remember how to free the memory block for when we're done sending it + p->id.ram = data; p->size = (UINT32)size; p->fileid = fileid; - p->next = NULL; // end of list + p->next = NULL; // End of list - DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->filename,p->size,node,fileid)); + DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->id.ram,p->size,node,fileid)); - filetosend++; + filestosend++; } -static void EndSend(INT32 node) +/** Stops sending a file for a node, and removes the file request from the list, + * either because the file has been fully sent or because the node was disconnected + * + * \param node The destination + * + */ +static void SV_EndFileSend(INT32 node) { filetx_t *p = transfer[node].txlist; + + // Free the file request according to the freemethod parameter used with SV_SendFile/Ram switch (p->ram) { - case SF_FILE: + case SF_FILE: // It's a file, close it and free its filename if (transfer[node].currentfile) fclose(transfer[node].currentfile); - free(p->filename); + free(p->id.filename); break; - case SF_Z_RAM: - Z_Free(p->filename); + case SF_Z_RAM: // It's a memory block allocated with Z_Alloc or the likes, use Z_Free + Z_Free(p->id.ram); break; - case SF_RAM: - free(p->filename); - case SF_NOFREERAM: + case SF_RAM: // It's a memory block allocated with malloc, use free + free(p->id.ram); + case SF_NOFREERAM: // Nothing to free break; } + + // Remove the file request from the list transfer[node].txlist = p->next; - transfer[node].currentfile = NULL; free(p); - filetosend--; + + // Indicate that the transmission is over + transfer[node].currentfile = NULL; + + filestosend--; } #define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH) -void FiletxTicker(void) +/** Handles file transmission + * + * + */ +void SV_FileSendTicker(void) { static INT32 currentnode = 0; filetx_pak *p; @@ -557,12 +646,12 @@ void FiletxTicker(void) filetx_t *f; INT32 packetsent = PACKETPERTIC, ram, i; - if (!filetosend) + if (!filestosend) return; if (!packetsent) packetsent++; // (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth) - while (packetsent-- && filetosend != 0) + while (packetsent-- && filestosend != 0) { for (i = currentnode, ram = 0; ram < MAXNETNODES; i = (i+1) % MAXNETNODES, ram++) @@ -571,24 +660,25 @@ void FiletxTicker(void) goto found; } // no transfer to do - I_Error("filetosend=%d but no filetosend found\n", filetosend); + I_Error("filestosend=%d but no file to send found\n", filestosend); found: currentnode = (i+1) % MAXNETNODES; f = transfer[i].txlist; ram = f->ram; - if (!transfer[i].currentfile) // file not already open + // Open the file if it isn't open yet, or + if (!transfer[i].currentfile) { - if (!ram) + if (!ram) // Sending a file { long filesize; transfer[i].currentfile = - fopen(f->filename, "rb"); + fopen(f->id.filename, "rb"); if (!transfer[i].currentfile) I_Error("File %s does not exist", - f->filename); + f->id.filename); fseek(transfer[i].currentfile, 0, SEEK_END); filesize = ftell(transfer[i].currentfile); @@ -596,45 +686,48 @@ void FiletxTicker(void) // 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); + I_Error("filesize of %s is too large", f->id.filename); + if (filesize == -1) + I_Error("Error getting filesize of %s", f->id.filename); f->size = (UINT32)filesize; fseek(transfer[i].currentfile, 0, SEEK_SET); } - else - transfer[i].currentfile = (FILE *)1; + else // Sending RAM + transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open transfer[i].position = 0; } + // Build a packet containing a file fragment 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); + M_Memcpy(p->data, &f->id.ram[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))); + I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, strerror(ferror(transfer[i].currentfile))); p->position = LONG(transfer[i].position); - // put flag so receiver know the totalsize + // Put flag so receiver knows the total size 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; + + // Send the packet + if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND + { // Success + transfer[i].position = (UINT32)(transfer[i].position + size); + if (transfer[i].position == f->size) // Finish? + SV_EndFileSend(i); } - else // success - { - transfer[i].position = (UINT32)(size+transfer[i].position); - if (transfer[i].position == f->size) // finish ? - EndSend(i); + else + { // 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; } } } @@ -646,16 +739,18 @@ void Got_Filetxpak(void) if (filenum >= fileneedednum) { - DEBFILE(va("fileframent not needed %d>%d\n",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); + if (fileneeded[filenum].file) + I_Error("Got_Filetxpak: already open file\n"); + fileneeded[filenum].file = fopen(fileneeded[filenum].filename, "wb"); + if (!fileneeded[filenum].file) + 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; } @@ -664,24 +759,24 @@ void Got_Filetxpak(void) { 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 ! + // Use a special trick to know when the file is complete (not always used) + // WARNING: file fragments can arrive out of order so don't stop yet! 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))); + // We can receive packet in the wrong order, anyway all os support gaped file + fseek(fileneeded[filenum].file, pos, SEEK_SET); + if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].file) != 1) + I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].file))); fileneeded[filenum].currentsize += size; - // finished? + // Finished? if (fileneeded[filenum].currentsize == fileneeded[filenum].totalsize) { - fclose(fileneeded[filenum].phandle); - fileneeded[filenum].phandle = NULL; + fclose(fileneeded[filenum].file); + fileneeded[filenum].file = NULL; fileneeded[filenum].status = FS_FOUND; CONS_Printf(M_GetText("Downloading %s...(done)\n"), fileneeded[filenum].filename); @@ -689,8 +784,8 @@ void Got_Filetxpak(void) } else I_Error("Received a file not requested\n"); - // send ack back quickly + // Send ack back quickly if (++filetime == 3) { Net_SendAcks(servernode); @@ -702,33 +797,39 @@ void Got_Filetxpak(void) #endif } -void AbortSendFiles(INT32 node) +/** Cancels all file requests for a node + * + * \param node The destination + * \sa SV_EndFileSend + * + */ +void SV_AbortSendFiles(INT32 node) { while (transfer[node].txlist) - EndSend(node); + SV_EndFileSend(node); } void CloseNetFile(void) { INT32 i; - // is sending? + // Is sending? for (i = 0; i < MAXNETNODES; i++) - AbortSendFiles(i); + SV_AbortSendFiles(i); - // receiving a file? + // Receiving a file? for (i = 0; i < MAX_WADFILES; i++) - if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].phandle) + if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file) { - fclose(fileneeded[i].phandle); - // file is not complete delete it + fclose(fileneeded[i].file); + // File is not complete delete it remove(fileneeded[i].filename); } - // remove FILEFRAGMENT from acknledge list + // Remove PT_FILEFRAGMENT from acknowledge list Net_AbortPacketType(PT_FILEFRAGMENT); } -// functions cut and pasted from doomatic :) +// Functions cut and pasted from Doomatic :) void nameonly(char *s) { diff --git a/src/d_netfil.h b/src/d_netfil.h index a68119f10..e82b57d67 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -29,21 +29,21 @@ typedef enum FS_FOUND, FS_REQUESTED, FS_DOWNLOADING, - FS_OPEN, // is opened and used in w_wad + FS_OPEN, // Is opened and used in w_wad FS_MD5SUMBAD } filestatus_t; typedef struct { UINT8 important; - UINT8 willsend; // is the server willing to send it? + UINT8 willsend; // Is the server willing to send it? char filename[MAX_WADPATH]; UINT8 md5sum[16]; - // used only for download - FILE *phandle; + // Used only for download + FILE *file; UINT32 currentsize; UINT32 totalsize; - filestatus_t status; // the value returned by recsearch + filestatus_t status; // The value returned by recsearch } fileneeded_t; extern INT32 fileneedednum; @@ -58,28 +58,24 @@ 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, +void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid); -void FiletxTicker(void); +void SV_FileSendTicker(void); void Got_Filetxpak(void); boolean CL_CheckDownloadable(void); boolean CL_SendRequestFile(void); void Got_RequestFilePak(INT32 node); -void AbortSendFiles(INT32 node); +void SV_AbortSendFiles(INT32 node); void CloseNetFile(void); boolean fileexist(char *filename, time_t ptime); -// search a file in the wadpath, return FS_FOUND when found +// Search a file in the wadpath, return FS_FOUND when found filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum, boolean completepath); filestatus_t checkfilemd5(char *filename, const UINT8 *wantedmd5sum); diff --git a/src/djgppdos/i_system.c b/src/djgppdos/i_system.c index 854d68f4d..dae9ed16e 100644 --- a/src/djgppdos/i_system.c +++ b/src/djgppdos/i_system.c @@ -1721,6 +1721,18 @@ INT32 I_PutEnv(char *variable) return putenv(variable); } +INT32 I_ClipboardCopy(const char *data, size_t size) +{ + (void)data; + (void)size; + return -1; +} + +char *I_ClipboardPaste(void) +{ + return NULL; +} + const CPUInfoFlags *I_CPUInfo(void) { static CPUInfoFlags DOS_CPUInfo; diff --git a/src/doomdef.h b/src/doomdef.h index fb8ab1ca2..4b2d8c737 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -394,6 +394,9 @@ extern INT32 cv_debug; // Misc stuff for later... // ======================= +// Modifier key variables, accessible anywhere +extern UINT8 shiftdown, ctrldown, altdown; + // if we ever make our alloc stuff... #define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL) diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c index c1e48b60b..a3fe3077c 100644 --- a/src/dummy/i_system.c +++ b/src/dummy/i_system.c @@ -162,6 +162,18 @@ INT32 I_PutEnv(char *variable) return -1; } +INT32 I_ClipboardCopy(const char *data, size_t size) +{ + (void)data; + (void)size; + return -1; +} + +char *I_ClipboardPaste(void) +{ + return NULL; +} + void I_RegisterSysCommands(void) {} #include "../sdl/dosstr.c" diff --git a/src/g_game.c b/src/g_game.c index f891b0105..84db90132 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -5579,7 +5579,7 @@ boolean G_CheckDemoStatus(void) WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file. #endif - saved = FIL_WriteFile(demoname, demobuffer, demo_p - demobuffer); // finally output the file. + saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file. free(demobuffer); demorecording = false; diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index 60183b58e..f23753ee5 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -656,6 +656,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color) { FOutVector v[4]; FSurfaceInfo Surf; + float sdupx, sdupy; if (w < 0 || h < 0) return; // consistency w/ software @@ -664,10 +665,16 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color) // | /| // |/ | // 0--1 - v[0].x = v[3].x = (x - 160.0f)/160.0f; - v[2].x = v[1].x = ((x+w) - 160.0f)/160.0f; - v[0].y = v[1].y = -(y - 100.0f)/100.0f; - v[2].y = v[3].y = -((y+h) - 100.0f)/100.0f; + sdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f; + sdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f; + + if (color & V_NOSCALESTART) + sdupx = sdupy = 2.0f; + + v[0].x = v[3].x = (x*sdupx)/vid.width - 1; + v[2].x = v[1].x = (x*sdupx + w*sdupx)/vid.width - 1; + v[0].y = v[1].y = 1-(y*sdupy)/vid.height; + v[2].y = v[3].y = 1-(y*sdupy + h*sdupy)/vid.height; //Hurdler: do we still use this argb color? if not, we should remove it v[0].argb = v[1].argb = v[2].argb = v[3].argb = 0xff00ff00; //; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 8e48ec110..4ec2e346a 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -308,6 +308,23 @@ static md2_model_t *md2_readModel(const char *filename) model->header.numSkins = 1; +#define MD2LIMITCHECK(field, max, msgname) \ + if (field > max) \ + { \ + CONS_Alert(CONS_ERROR, "md2_readModel: %s has too many " msgname " (# found: %d, maximum: %d)\n", filename, field, max); \ + md2_freeModel (model); \ + return 0; \ + } + + // Uncomment if these are actually needed +// MD2LIMITCHECK(model->header.numSkins, MD2_MAX_SKINS, "skins") +// MD2LIMITCHECK(model->header.numTexCoords, MD2_MAX_TEXCOORDS, "texture coordinates") + MD2LIMITCHECK(model->header.numTriangles, MD2_MAX_TRIANGLES, "triangles") + MD2LIMITCHECK(model->header.numFrames, MD2_MAX_FRAMES, "frames") + MD2LIMITCHECK(model->header.numVertices, MD2_MAX_VERTICES, "vertices") + +#undef MD2LIMITCHECK + // read skins fseek(file, model->header.offsetSkins, SEEK_SET); if (model->header.numSkins > 0) @@ -319,8 +336,6 @@ static md2_model_t *md2_readModel(const char *filename) md2_freeModel (model); return 0; } - - ; } // read texture coordinates @@ -334,8 +349,6 @@ static md2_model_t *md2_readModel(const char *filename) md2_freeModel (model); return 0; } - - } // read triangles @@ -769,6 +782,7 @@ void HWR_InitMD2(void) md2_playermodels[s].grpatch = NULL; md2_playermodels[s].skin = -1; md2_playermodels[s].notfound = true; + md2_playermodels[s].error = false; } for (i = 0; i < NUMSPRITES; i++) { @@ -777,6 +791,7 @@ void HWR_InitMD2(void) md2_models[i].grpatch = NULL; md2_models[i].skin = -1; md2_models[i].notfound = true; + md2_models[i].error = false; } // read the md2.dat file @@ -1269,6 +1284,8 @@ void HWR_DrawMD2(gr_vissprite_t *spr) else md2 = &md2_models[spr->mobj->sprite]; + if (md2->error) + return; // we already failed loading this before :( if (!md2->model) { //CONS_Debug(DBG_RENDER, "Loading MD2... (%s)", sprnames[spr->mobj->sprite]); @@ -1282,6 +1299,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr) else { //CONS_Debug(DBG_RENDER, " FAILED\n"); + md2->error = true; // prevent endless fail return; } } diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h index 36078268b..5a7e6d2b3 100644 --- a/src/hardware/hw_md2.h +++ b/src/hardware/hw_md2.h @@ -123,6 +123,7 @@ typedef struct void *blendgrpatch; boolean notfound; INT32 skin; + boolean error; } md2_t; extern md2_t md2_models[NUMSPRITES]; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index ec747305e..a57566910 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -757,15 +757,8 @@ void HU_clearChatChars(void) // boolean HU_Responder(event_t *ev) { - static boolean shiftdown = false; UINT8 c; - if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT) - { - shiftdown = (ev->type == ev_keydown); - return chat_on; - } - if (ev->type != ev_keydown) return false; @@ -797,6 +790,14 @@ boolean HU_Responder(event_t *ev) } else // if chat_on { + // Ignore modifier keys + // Note that we do this here so users can still set + // their chat keys to one of these, if they so desire. + if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT + || ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL + || ev->data1 == KEY_LALT || ev->data1 == KEY_RALT) + return true; + c = (UINT8)ev->data1; // use console translations diff --git a/src/i_system.h b/src/i_system.h index c161851e0..d61f2d16e 100644 --- a/src/i_system.h +++ b/src/i_system.h @@ -296,6 +296,14 @@ char *I_GetEnv(const char *name); INT32 I_PutEnv(char *variable); +/** \brief Put data in system clipboard +*/ +INT32 I_ClipboardCopy(const char *data, size_t size); + +/** \brief Retrieve data from system clipboard +*/ +const char *I_ClipboardPaste(void); + void I_RegisterSysCommands(void); #endif diff --git a/src/lua_hook.h b/src/lua_hook.h index 804d99e12..53e0a7d8e 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -60,7 +60,7 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which); #define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type #define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type -#define LUAh_MobjThinker(mo) LUAh_MobjHook(mo, hook_MobjThinker) // Hook for P_MobjThinker or P_SceneryThinker by mobj type +boolean LUAh_MobjThinker(mobj_t *mo); // Hook for P_MobjThinker or P_SceneryThinker by mobj type #define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Should mobj take damage?) boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 1b9652571..a24473bad 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -74,12 +74,30 @@ typedef struct hook_s* hook_p; #define FMT_HOOKID "hook_%d" +// For each mobj type, a linked list to its thinker and collision hooks. +// That way, we don't have to iterate through all the hooks. +// We could do that with all other mobj hooks, but it would probably just be +// a waste of memory since they are only called occasionally. Probably... +static hook_p mobjthinkerhooks[NUMMOBJTYPES]; +static hook_p mobjcollidehooks[NUMMOBJTYPES]; + +// For each mobj type, a linked list for other mobj hooks +static hook_p mobjhooks[NUMMOBJTYPES]; + +// A linked list for player hooks +static hook_p playerhooks; + +// A linked list for linedef executor hooks +static hook_p linedefexecutorhooks; + +// For other hooks, a unique linked list hook_p roothook; // Takes hook, function, and additional arguments (mobj type to act on, etc.) static int lib_addHook(lua_State *L) { static struct hook_s hook = {NULL, 0, 0, {0}, false}; + static UINT32 nextid; hook_p hookp, *lastp; hook.type = luaL_checkoption(L, 1, NULL, hookNames); @@ -109,6 +127,7 @@ static int lib_addHook(lua_State *L) hook.s.mt = MT_NULL; if (lua_isnumber(L, 2)) hook.s.mt = lua_tonumber(L, 2); + luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t"); break; case hook_BotAI: hook.s.skinname = NULL; @@ -141,18 +160,49 @@ static int lib_addHook(lua_State *L) hooksAvailable[hook.type/8] |= 1<<(hook.type%8); - // iterate the hook metadata structs // set hook.id to the highest id + 1 - // set lastp to the last hook struct's "next" pointer. - lastp = &roothook; - hook.id = 0; - for (hookp = roothook; hookp; hookp = hookp->next) + hook.id = nextid++; + + // Special cases for some hook types (see the comments above mobjthinkerhooks declaration) + switch(hook.type) { - if (hookp->id >= hook.id) - hook.id = hookp->id+1; - lastp = &hookp->next; + case hook_MobjThinker: + lastp = &mobjthinkerhooks[hook.s.mt]; + break; + case hook_MobjCollide: + case hook_MobjMoveCollide: + lastp = &mobjcollidehooks[hook.s.mt]; + break; + case hook_MobjSpawn: + case hook_TouchSpecial: + case hook_MobjFuse: + case hook_BossThinker: + case hook_ShouldDamage: + case hook_MobjDamage: + case hook_MobjDeath: + case hook_BossDeath: + case hook_MobjRemoved: + lastp = &mobjhooks[hook.s.mt]; + break; + case hook_JumpSpecial: + case hook_AbilitySpecial: + case hook_SpinSpecial: + case hook_JumpSpinSpecial: + case hook_PlayerSpawn: + lastp = &playerhooks; + break; + case hook_LinedefExecute: + lastp = &linedefexecutorhooks; + break; + default: + lastp = &roothook; + break; } + // iterate the hook metadata structs + // set lastp to the last hook struct's "next" pointer. + for (hookp = *lastp; hookp; hookp = hookp->next) + lastp = &hookp->next; // allocate a permanent memory struct to stuff hook. hookp = ZZ_Alloc(sizeof(struct hook_s)); memcpy(hookp, &hook, sizeof(struct hook_s)); @@ -183,9 +233,29 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which) lua_settop(gL, 0); - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == which - && (hookp->s.mt == MT_NULL || hookp->s.mt == mo->type)) + // Look for all generic mobj hooks + for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) + if (hookp->type == which) + { + if (lua_gettop(gL) == 0) + LUA_PushUserdata(gL, mo, META_MOBJ); + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + lua_pushvalue(gL, -2); + if (lua_pcall(gL, 1, 1, 0)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (lua_toboolean(gL, -1)) + hooked = true; + lua_pop(gL, 1); + } + + for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next) + if (hookp->type == which) { if (lua_gettop(gL) == 0) LUA_PushUserdata(gL, mo, META_MOBJ); @@ -217,7 +287,7 @@ boolean LUAh_PlayerHook(player_t *plr, enum hook which) lua_settop(gL, 0); - for (hookp = roothook; hookp; hookp = hookp->next) + for (hookp = playerhooks; hookp; hookp = hookp->next) if (hookp->type == which) { if (lua_gettop(gL) == 0) @@ -338,9 +408,38 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) lua_settop(gL, 0); - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == which - && (hookp->s.mt == MT_NULL || hookp->s.mt == thing1->type)) + // Look for all generic mobj collision hooks + for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next) + if (hookp->type == which) + { + if (lua_gettop(gL) == 0) + { + LUA_PushUserdata(gL, thing1, META_MOBJ); + LUA_PushUserdata(gL, thing2, META_MOBJ); + } + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + lua_pushvalue(gL, -3); + lua_pushvalue(gL, -3); + if (lua_pcall(gL, 2, 1, 0)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (!lua_isnil(gL, -1)) + { // if nil, leave shouldCollide = 0. + if (lua_toboolean(gL, -1)) + shouldCollide = 1; // Force yes + else + shouldCollide = 2; // Force no + } + lua_pop(gL, 1); + } + + for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next) + if (hookp->type == which) { if (lua_gettop(gL) == 0) { @@ -372,6 +471,59 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) return shouldCollide; } +// Hook for mobj thinkers +boolean LUAh_MobjThinker(mobj_t *mo) +{ + hook_p hookp; + boolean hooked = false; + if (!gL || !(hooksAvailable[hook_MobjThinker/8] & (1<<(hook_MobjThinker%8)))) + return false; + + lua_settop(gL, 0); + + // Look for all generic mobj thinker hooks + for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next) + { + if (lua_gettop(gL) == 0) + LUA_PushUserdata(gL, mo, META_MOBJ); + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + lua_pushvalue(gL, -2); + if (lua_pcall(gL, 1, 1, 0)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (lua_toboolean(gL, -1)) + hooked = true; + lua_pop(gL, 1); + } + + for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next) + { + if (lua_gettop(gL) == 0) + LUA_PushUserdata(gL, mo, META_MOBJ); + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + lua_pushvalue(gL, -2); + if (lua_pcall(gL, 1, 1, 0)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (lua_toboolean(gL, -1)) + hooked = true; + lua_pop(gL, 1); + } + + lua_settop(gL, 0); + return hooked; +} + // Hook for P_TouchSpecialThing by mobj type boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher) { @@ -382,9 +534,33 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher) lua_settop(gL, 0); - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_TouchSpecial - && (hookp->s.mt == MT_NULL || hookp->s.mt == special->type)) + // Look for all generic touch special hooks + for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) + if (hookp->type == hook_TouchSpecial) + { + if (lua_gettop(gL) == 0) + { + LUA_PushUserdata(gL, special, META_MOBJ); + LUA_PushUserdata(gL, toucher, META_MOBJ); + } + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + lua_pushvalue(gL, -3); + lua_pushvalue(gL, -3); + if (lua_pcall(gL, 2, 1, 0)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (lua_toboolean(gL, -1)) + hooked = true; + lua_pop(gL, 1); + } + + for (hookp = mobjhooks[special->type]; hookp; hookp = hookp->next) + if (hookp->type == hook_TouchSpecial) { if (lua_gettop(gL) == 0) { @@ -421,9 +597,42 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 lua_settop(gL, 0); - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_ShouldDamage - && (hookp->s.mt == MT_NULL || hookp->s.mt == target->type)) + // Look for all generic should damage hooks + for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) + if (hookp->type == hook_ShouldDamage) + { + if (lua_gettop(gL) == 0) + { + LUA_PushUserdata(gL, target, META_MOBJ); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + lua_pushinteger(gL, damage); + } + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + lua_pushvalue(gL, -5); + lua_pushvalue(gL, -5); + lua_pushvalue(gL, -5); + lua_pushvalue(gL, -5); + if (lua_pcall(gL, 4, 1, 0)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (!lua_isnil(gL, -1)) + { + if (lua_toboolean(gL, -1)) + shouldDamage = 1; // Force yes + else + shouldDamage = 2; // Force no + } + lua_pop(gL, 1); + } + + for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) + if (hookp->type == hook_ShouldDamage) { if (lua_gettop(gL) == 0) { @@ -469,9 +678,37 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 lua_settop(gL, 0); - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_MobjDamage - && (hookp->s.mt == MT_NULL || hookp->s.mt == target->type)) + // Look for all generic mobj damage hooks + for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) + if (hookp->type == hook_MobjDamage) + { + if (lua_gettop(gL) == 0) + { + LUA_PushUserdata(gL, target, META_MOBJ); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + lua_pushinteger(gL, damage); + } + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + lua_pushvalue(gL, -5); + lua_pushvalue(gL, -5); + lua_pushvalue(gL, -5); + lua_pushvalue(gL, -5); + if (lua_pcall(gL, 4, 1, 0)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (lua_toboolean(gL, -1)) + hooked = true; + lua_pop(gL, 1); + } + + for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) + if (hookp->type == hook_MobjDamage) { if (lua_gettop(gL) == 0) { @@ -512,9 +749,35 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source) lua_settop(gL, 0); - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_MobjDeath - && (hookp->s.mt == MT_NULL || hookp->s.mt == target->type)) + // Look for all generic mobj death hooks + for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) + if (hookp->type == hook_MobjDeath) + { + if (lua_gettop(gL) == 0) + { + LUA_PushUserdata(gL, target, META_MOBJ); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + } + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + lua_pushvalue(gL, -4); + lua_pushvalue(gL, -4); + lua_pushvalue(gL, -4); + if (lua_pcall(gL, 3, 1, 0)) { + if (!hookp->error || cv_debug & DBG_LUA) + CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); + lua_pop(gL, 1); + hookp->error = true; + continue; + } + if (lua_toboolean(gL, -1)) + hooked = true; + lua_pop(gL, 1); + } + + for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) + if (hookp->type == hook_MobjDeath) { if (lua_gettop(gL) == 0) { @@ -652,9 +915,8 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) lua_settop(gL, 0); - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_LinedefExecute - && !strcmp(hookp->s.funcname, line->text)) + for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next) + if (!strcmp(hookp->s.funcname, line->text)) { if (lua_gettop(gL) == 0) { diff --git a/src/m_fixed.h b/src/m_fixed.h index 70402f27a..34fd25db2 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -46,41 +46,6 @@ typedef INT32 fixed_t; #define FLOAT_TO_FIXED(f) (fixed_t)((f) * ((float)FRACUNIT)) -/** \brief The TMulScale16 function - - \param a a parameter of type fixed_t - \param b a parameter of type fixed_t - \param c a parameter of type fixed_t - \param d a parameter of type fixed_t - \param e a parameter of type fixed_t - \param f a parameter of type fixed_t - - \return fixed_t - - -*/ -FUNCMATH FUNCINLINE static ATTRINLINE fixed_t TMulScale16(fixed_t a, fixed_t b, fixed_t c, fixed_t d, fixed_t e, fixed_t f) \ -{ \ - return (fixed_t)((((INT64)a * (INT64)b) + ((INT64)c * (INT64)d) \ - + ((INT64)e * (INT64)f)) >> 16); \ -} - -/** \brief The DMulScale16 function - - \param a a parameter of type fixed_t - \param b a parameter of type fixed_t - \param c a parameter of type fixed_t - \param d a parameter of type fixed_t - - \return fixed_t - - -*/ -FUNCMATH FUNCINLINE static ATTRINLINE fixed_t DMulScale16(fixed_t a, fixed_t b, fixed_t c, fixed_t d) \ -{ \ - return (fixed_t)((((INT64)a * (INT64)b) + ((INT64)c * (INT64)d)) >> 16); \ -} - #if defined (__WATCOMC__) && FRACBITS == 16 #pragma aux FixedMul = \ "imul ebx", \ diff --git a/src/m_menu.c b/src/m_menu.c index 78c381273..d7b4d9080 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -182,9 +182,6 @@ static INT32 vidm_selected = 0; static INT32 vidm_nummodes; static INT32 vidm_column_size; -// what a headache. -static boolean shiftdown = false; - // // PROTOTYPES // @@ -2080,11 +2077,6 @@ boolean M_Responder(event_t *ev) || gamestate == GS_CREDITS || gamestate == GS_EVALUATION) return false; - if (ev->type == ev_keyup && (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)) - { - shiftdown = false; - return false; - } if (noFurtherInput) { // Ignore input after enter/escape/other buttons @@ -2098,10 +2090,6 @@ boolean M_Responder(event_t *ev) // added 5-2-98 remap virtual keys (mouse & joystick buttons) switch (ch) { - case KEY_LSHIFT: - case KEY_RSHIFT: - shiftdown = true; - break; //return false; case KEY_MOUSE1: case KEY_JOY1: case KEY_JOY1 + 2: diff --git a/src/nds/i_system.c b/src/nds/i_system.c index 0ed58029c..3e5c4b8c6 100644 --- a/src/nds/i_system.c +++ b/src/nds/i_system.c @@ -269,6 +269,18 @@ INT32 I_PutEnv(char *variable) return -1; } +INT32 I_ClipboardCopy(const char *data, size_t size) +{ + (void)data; + (void)size; + return -1; +} + +char *I_ClipboardPaste(void) +{ + return NULL; +} + void I_RegisterSysCommands(void) {} #include "../sdl/dosstr.c" diff --git a/src/p_setup.c b/src/p_setup.c index e56c44c70..111c717d3 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2585,7 +2585,7 @@ boolean P_SetupLevel(boolean skipprecip) lastloadedmaplumpnum = W_GetNumForName(maplumpname = G_BuildMapName(gamemap)); R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette); - CON_ReSetupBackColormap(mapheaderinfo[gamemap-1]->palette); + CON_SetupBackColormap(); // now part of level loading since in future each level may have // its own anim texture sequences, switches etc. diff --git a/src/r_defs.h b/src/r_defs.h index 2c5860ee7..3669ec8f3 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -224,15 +224,6 @@ typedef struct linechain_s -// ZDoom C++ to Legacy C conversion Tails 04-29-2002 (for slopes) -typedef struct secplane_t -{ - // the plane is defined as a*x + b*y + c*z + d = 0 - // ic is 1/c, for faster Z calculations - - fixed_t a, b, c, d, ic; -} secplane_t; - // Slopes #ifdef ESLOPE typedef enum { diff --git a/src/r_main.c b/src/r_main.c index 498f4dab8..e1fe0dce7 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -366,69 +366,6 @@ fixed_t R_PointToDist(fixed_t x, fixed_t y) return R_PointToDist2(viewx, viewy, x, y); } -/*************************************** -*** Zdoom C++ to Legacy C conversion *** -****************************************/ - -// Utility to find the Z height at an XY location in a sector (for slopes) -fixed_t R_SecplaneZatPoint(secplane_t *secplane, fixed_t x, fixed_t y) -{ - return FixedMul(secplane->ic, -secplane->d - DMulScale16(secplane->a, x, secplane->b, y)); -} - -// Returns the value of z at (x,y) if d is equal to dist -fixed_t R_SecplaneZatPointDist (secplane_t *secplane, fixed_t x, fixed_t y, fixed_t dist) -{ - return FixedMul(secplane->ic, -dist - DMulScale16(secplane->a, x, secplane->b, y)); -} - -// Flips the plane's vertical orientiation, so that if it pointed up, -// it will point down, and vice versa. -void R_SecplaneFlipVert(secplane_t *secplane) -{ - secplane->a = -secplane->a; - secplane->b = -secplane->b; - secplane->c = -secplane->c; - secplane->d = -secplane->d; - secplane->ic = -secplane->ic; -} - -// Returns true if 2 planes are the same -boolean R_ArePlanesSame(secplane_t *original, secplane_t *other) -{ - return original->a == other->a && original->b == other->b - && original->c == other->c && original->d == other->d; -} - -// Returns true if 2 planes are different -boolean R_ArePlanesDifferent(secplane_t *original, secplane_t *other) -{ - return original->a != other->a || original->b != other->b - || original->c != other->c || original->d != other->d; -} - -// Moves a plane up/down by hdiff units -void R_SecplaneChangeHeight(secplane_t *secplane, fixed_t hdiff) -{ - secplane->d = secplane->d - FixedMul(hdiff, secplane->c); -} - -// Returns how much this plane's height would change if d were set to oldd -fixed_t R_SecplaneHeightDiff(secplane_t *secplane, fixed_t oldd) -{ - return FixedMul(oldd - secplane->d, secplane->ic); -} - -fixed_t R_SecplanePointToDist(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z) -{ - return -TMulScale16(secplane->a, x, y, secplane->b, z, secplane->c); -} - -fixed_t R_SecplanePointToDist2(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z) -{ - return -TMulScale16(secplane->a, x, secplane->b, y, z, secplane->c); -} - // // R_ScaleFromGlobalAngle // Returns the texture mapping scale for the current line (horizontal span) diff --git a/src/r_main.h b/src/r_main.h index 8f46a938e..2e768cb9c 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -61,18 +61,6 @@ angle_t R_PointToAngle2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1); fixed_t R_PointToDist(fixed_t x, fixed_t y); fixed_t R_PointToDist2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1); -// ZDoom C++ to Legacy C conversion Tails 04-29-2002 -fixed_t R_SecplaneZatPoint(secplane_t *secplane, fixed_t x, fixed_t y); -fixed_t R_SecplaneZatPointDist(secplane_t *secplane, fixed_t x, fixed_t y, - fixed_t dist); -void R_SecplaneFlipVert(secplane_t *secplane); -boolean R_ArePlanesSame(secplane_t *original, secplane_t *other); -boolean R_ArePlanesDifferent(secplane_t *original, secplane_t *other); -void R_SecplaneChangeHeight(secplane_t *secplane, fixed_t hdiff); -fixed_t R_SecplaneHeightDiff(secplane_t *secplane, fixed_t oldd); -fixed_t R_SecplanePointToDist(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z); -fixed_t R_SecplanePointToDist2(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z); - fixed_t R_ScaleFromGlobalAngle(angle_t visangle); subsector_t *R_PointInSubsector(fixed_t x, fixed_t y); subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y); diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index ea8ade8c1..71ee3f794 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -2647,6 +2647,47 @@ INT32 I_PutEnv(char *variable) #endif } +INT32 I_ClipboardCopy(const char *data, size_t size) +{ + char storage[256]; + if (size > 255) + size = 255; + memcpy(storage, data, size); + storage[size] = 0; + + if (SDL_SetClipboardText(storage)) + return 0; + return -1; +} + +const char *I_ClipboardPaste(void) +{ + static char clipboard_modified[256]; + char *clipboard_contents, *i = clipboard_modified; + + if (!SDL_HasClipboardText()) + return NULL; + clipboard_contents = SDL_GetClipboardText(); + memcpy(clipboard_modified, clipboard_contents, 255); + SDL_free(clipboard_contents); + clipboard_modified[255] = 0; + + while (*i) + { + if (*i == '\n' || *i == '\r') + { // End on newline + *i = 0; + break; + } + else if (*i == '\t') + *i = ' '; // Tabs become spaces + else if (*i < 32 || (unsigned)*i > 127) + *i = '?'; // Nonprintable chars become question marks + ++i; + } + return (const char *)&clipboard_modified; +} + /** \brief The isWadPathOk function \param path string path to check diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 71baca510..aa572e6e0 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -33,14 +33,6 @@ #pragma warning(default : 4214 4244) #endif -#if SDL_VERSION_ATLEAST(1,3,0) -#define SDLK_EQUALS SDLK_KP_EQUALSAS400 -#define SDLK_LMETA SDLK_LGUI -#define SDLK_RMETA SDLK_RGUI -#else -#define HAVE_SDLMETAKEYS -#endif - #ifdef HAVE_TTF #include "i_ttf.h" #endif @@ -189,14 +181,14 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen) wasfullscreen = SDL_TRUE; SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); } - else if (!fullscreen && wasfullscreen) + else if (wasfullscreen) { wasfullscreen = SDL_FALSE; SDL_SetWindowFullscreen(window, 0); SDL_SetWindowSize(window, width, height); SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(1), SDL_WINDOWPOS_CENTERED_DISPLAY(1)); } - else if (!wasfullscreen) + else { // Reposition window only in windowed mode SDL_SetWindowSize(window, width, height); @@ -282,129 +274,70 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code) } switch (code) { - case SDL_SCANCODE_F11: // F11 and F12 are - return KEY_F11; // separated from the - case SDL_SCANCODE_F12: // rest of the function - return KEY_F12; // keys + // F11 and F12 are separated from the rest of the function keys + case SDL_SCANCODE_F11: return KEY_F11; + case SDL_SCANCODE_F12: return KEY_F12; - case SDL_SCANCODE_KP_0: - return KEY_KEYPAD0; - case SDL_SCANCODE_KP_1: - return KEY_KEYPAD1; - case SDL_SCANCODE_KP_2: - return KEY_KEYPAD2; - case SDL_SCANCODE_KP_3: - return KEY_KEYPAD3; - case SDL_SCANCODE_KP_4: - return KEY_KEYPAD4; - case SDL_SCANCODE_KP_5: - return KEY_KEYPAD5; - case SDL_SCANCODE_KP_6: - return KEY_KEYPAD6; - case SDL_SCANCODE_KP_7: - return KEY_KEYPAD7; - case SDL_SCANCODE_KP_8: - return KEY_KEYPAD8; - case SDL_SCANCODE_KP_9: - return KEY_KEYPAD9; + case SDL_SCANCODE_KP_0: return KEY_KEYPAD0; + case SDL_SCANCODE_KP_1: return KEY_KEYPAD1; + case SDL_SCANCODE_KP_2: return KEY_KEYPAD2; + case SDL_SCANCODE_KP_3: return KEY_KEYPAD3; + case SDL_SCANCODE_KP_4: return KEY_KEYPAD4; + case SDL_SCANCODE_KP_5: return KEY_KEYPAD5; + case SDL_SCANCODE_KP_6: return KEY_KEYPAD6; + case SDL_SCANCODE_KP_7: return KEY_KEYPAD7; + case SDL_SCANCODE_KP_8: return KEY_KEYPAD8; + case SDL_SCANCODE_KP_9: return KEY_KEYPAD9; - case SDL_SCANCODE_RETURN: - return KEY_ENTER; - case SDL_SCANCODE_ESCAPE: - return KEY_ESCAPE; - case SDL_SCANCODE_BACKSPACE: - return KEY_BACKSPACE; - case SDL_SCANCODE_TAB: - return KEY_TAB; - case SDL_SCANCODE_SPACE: - return KEY_SPACE; - case SDL_SCANCODE_MINUS: - return KEY_MINUS; - case SDL_SCANCODE_EQUALS: - return KEY_EQUALS; - case SDL_SCANCODE_LEFTBRACKET: - return '['; - case SDL_SCANCODE_RIGHTBRACKET: - return ']'; - case SDL_SCANCODE_BACKSLASH: - return '\\'; - case SDL_SCANCODE_NONUSHASH: - return '#'; - case SDL_SCANCODE_SEMICOLON: - return ';'; - case SDL_SCANCODE_APOSTROPHE: - return '\''; - case SDL_SCANCODE_GRAVE: - return '`'; - case SDL_SCANCODE_COMMA: - return ','; - case SDL_SCANCODE_PERIOD: - return '.'; - case SDL_SCANCODE_SLASH: - return '/'; - case SDL_SCANCODE_CAPSLOCK: - return KEY_CAPSLOCK; - case SDL_SCANCODE_PRINTSCREEN: - return 0; // undefined? - case SDL_SCANCODE_SCROLLLOCK: - return KEY_SCROLLLOCK; - case SDL_SCANCODE_PAUSE: - return KEY_PAUSE; - case SDL_SCANCODE_INSERT: - return KEY_INS; - case SDL_SCANCODE_HOME: - return KEY_HOME; - case SDL_SCANCODE_PAGEUP: - return KEY_PGUP; - case SDL_SCANCODE_DELETE: - return KEY_DEL; - case SDL_SCANCODE_END: - return KEY_END; - case SDL_SCANCODE_PAGEDOWN: - return KEY_PGDN; - case SDL_SCANCODE_RIGHT: - return KEY_RIGHTARROW; - case SDL_SCANCODE_LEFT: - return KEY_LEFTARROW; - case SDL_SCANCODE_DOWN: - return KEY_DOWNARROW; - case SDL_SCANCODE_UP: - return KEY_UPARROW; - case SDL_SCANCODE_NUMLOCKCLEAR: - return KEY_NUMLOCK; - case SDL_SCANCODE_KP_DIVIDE: - return KEY_KPADSLASH; - case SDL_SCANCODE_KP_MULTIPLY: - return '*'; // undefined? - case SDL_SCANCODE_KP_MINUS: - return KEY_MINUSPAD; - case SDL_SCANCODE_KP_PLUS: - return KEY_PLUSPAD; - case SDL_SCANCODE_KP_ENTER: - return KEY_ENTER; - case SDL_SCANCODE_KP_PERIOD: - return KEY_KPADDEL; - case SDL_SCANCODE_NONUSBACKSLASH: - return '\\'; + case SDL_SCANCODE_RETURN: return KEY_ENTER; + case SDL_SCANCODE_ESCAPE: return KEY_ESCAPE; + case SDL_SCANCODE_BACKSPACE: return KEY_BACKSPACE; + case SDL_SCANCODE_TAB: return KEY_TAB; + case SDL_SCANCODE_SPACE: return KEY_SPACE; + case SDL_SCANCODE_MINUS: return KEY_MINUS; + case SDL_SCANCODE_EQUALS: return KEY_EQUALS; + case SDL_SCANCODE_LEFTBRACKET: return '['; + case SDL_SCANCODE_RIGHTBRACKET: return ']'; + case SDL_SCANCODE_BACKSLASH: return '\\'; + case SDL_SCANCODE_NONUSHASH: return '#'; + case SDL_SCANCODE_SEMICOLON: return ';'; + case SDL_SCANCODE_APOSTROPHE: return '\''; + case SDL_SCANCODE_GRAVE: return '`'; + case SDL_SCANCODE_COMMA: return ','; + case SDL_SCANCODE_PERIOD: return '.'; + case SDL_SCANCODE_SLASH: return '/'; + case SDL_SCANCODE_CAPSLOCK: return KEY_CAPSLOCK; + case SDL_SCANCODE_PRINTSCREEN: return 0; // undefined? + case SDL_SCANCODE_SCROLLLOCK: return KEY_SCROLLLOCK; + case SDL_SCANCODE_PAUSE: return KEY_PAUSE; + case SDL_SCANCODE_INSERT: return KEY_INS; + case SDL_SCANCODE_HOME: return KEY_HOME; + case SDL_SCANCODE_PAGEUP: return KEY_PGUP; + case SDL_SCANCODE_DELETE: return KEY_DEL; + case SDL_SCANCODE_END: return KEY_END; + case SDL_SCANCODE_PAGEDOWN: return KEY_PGDN; + case SDL_SCANCODE_RIGHT: return KEY_RIGHTARROW; + case SDL_SCANCODE_LEFT: return KEY_LEFTARROW; + case SDL_SCANCODE_DOWN: return KEY_DOWNARROW; + case SDL_SCANCODE_UP: return KEY_UPARROW; + case SDL_SCANCODE_NUMLOCKCLEAR: return KEY_NUMLOCK; + case SDL_SCANCODE_KP_DIVIDE: return KEY_KPADSLASH; + case SDL_SCANCODE_KP_MULTIPLY: return '*'; // undefined? + case SDL_SCANCODE_KP_MINUS: return KEY_MINUSPAD; + case SDL_SCANCODE_KP_PLUS: return KEY_PLUSPAD; + case SDL_SCANCODE_KP_ENTER: return KEY_ENTER; + case SDL_SCANCODE_KP_PERIOD: return KEY_KPADDEL; + case SDL_SCANCODE_NONUSBACKSLASH: return '\\'; - case SDL_SCANCODE_LSHIFT: - return KEY_LSHIFT; - case SDL_SCANCODE_RSHIFT: - return KEY_RSHIFT; - case SDL_SCANCODE_LCTRL: - return KEY_LCTRL; - case SDL_SCANCODE_RCTRL: - return KEY_RCTRL; - case SDL_SCANCODE_LALT: - return KEY_LALT; - case SDL_SCANCODE_RALT: - return KEY_RALT; - case SDL_SCANCODE_LGUI: - return KEY_LEFTWIN; - case SDL_SCANCODE_RGUI: - return KEY_RIGHTWIN; - default: - break; + case SDL_SCANCODE_LSHIFT: return KEY_LSHIFT; + case SDL_SCANCODE_RSHIFT: return KEY_RSHIFT; + case SDL_SCANCODE_LCTRL: return KEY_LCTRL; + case SDL_SCANCODE_RCTRL: return KEY_RCTRL; + case SDL_SCANCODE_LALT: return KEY_LALT; + case SDL_SCANCODE_RALT: return KEY_RALT; + case SDL_SCANCODE_LGUI: return KEY_LEFTWIN; + case SDL_SCANCODE_RGUI: return KEY_RIGHTWIN; + default: break; } #ifdef HWRENDER DBG_Printf("Unknown incoming scancode: %d, represented %c\n", @@ -432,15 +365,10 @@ static void VID_Command_NumModes_f (void) CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), VID_NumModes()); } +// SDL2 doesn't have SDL_GetVideoSurface or a lot of the SDL_Surface flags that SDL 1.2 had static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText) { -#if 1 - (void)infoSurface; - (void)SurfaceText; - SDL2STUB(); -#else INT32 vfBPP; - const SDL_Surface *VidSur = SDL_GetVideoSurface(); if (!infoSurface) return; @@ -453,49 +381,12 @@ static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText) CONS_Printf("\x82" "%s\n", SurfaceText); CONS_Printf(M_GetText(" %ix%i at %i bit color\n"), infoSurface->w, infoSurface->h, vfBPP); - if (infoSurface->flags&SDL_HWSURFACE) - CONS_Printf("%s", M_GetText(" Stored in video memory\n")); - else if (infoSurface->flags&SDL_OPENGL) - CONS_Printf("%s", M_GetText(" Stored in an OpenGL context\n")); - else if (infoSurface->flags&SDL_PREALLOC) + if (infoSurface->flags&SDL_PREALLOC) CONS_Printf("%s", M_GetText(" Uses preallocated memory\n")); else CONS_Printf("%s", M_GetText(" Stored in system memory\n")); - - if (infoSurface->flags&SDL_ASYNCBLIT) - CONS_Printf("%s", M_GetText(" Uses asynchronous blits if possible\n")); - else - CONS_Printf("%s", M_GetText(" Uses synchronous blits if possible\n")); - - if (infoSurface->flags&SDL_ANYFORMAT) - CONS_Printf("%s", M_GetText(" Allows any pixel-format\n")); - - if (infoSurface->flags&SDL_HWPALETTE) - CONS_Printf("%s", M_GetText(" Has exclusive palette access\n")); - else if (VidSur == infoSurface) - CONS_Printf("%s", M_GetText(" Has nonexclusive palette access\n")); - - if (infoSurface->flags&SDL_DOUBLEBUF) - CONS_Printf("%s", M_GetText(" Double buffered\n")); - else if (VidSur == infoSurface) - CONS_Printf("%s", M_GetText(" No hardware flipping\n")); - - if (infoSurface->flags&SDL_FULLSCREEN) - CONS_Printf("%s", M_GetText(" Full screen\n")); - else if (infoSurface->flags&SDL_RESIZABLE) - CONS_Printf("%s", M_GetText(" Resizable window\n")); - else if (VidSur == infoSurface) - CONS_Printf("%s", M_GetText(" Nonresizable window\n")); - - if (infoSurface->flags&SDL_HWACCEL) - CONS_Printf("%s", M_GetText(" Uses hardware acceleration blit\n")); - if (infoSurface->flags&SDL_SRCCOLORKEY) - CONS_Printf("%s", M_GetText(" Use colorkey blitting\n")); if (infoSurface->flags&SDL_RLEACCEL) CONS_Printf("%s", M_GetText(" Colorkey RLE acceleration blit\n")); - if (infoSurface->flags&SDL_SRCALPHA) - CONS_Printf("%s", M_GetText(" Use alpha blending acceleration blit\n")); -#endif } static void VID_Command_Info_f (void) @@ -579,23 +470,6 @@ static void VID_Command_Mode_f (void) setmodeneeded = modenum+1; // request vid mode change } -#if 0 -#if defined(RPC_NO_WINDOWS_H) -static VOID MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - UNREFERENCED_PARAMETER(hWnd); - UNREFERENCED_PARAMETER(message); - UNREFERENCED_PARAMETER(wParam); - switch (message) - { - case WM_SETTEXT: - COM_BufAddText((LPCSTR)lParam); - break; - } -} -#endif -#endif - static inline void SDLJoyRemap(event_t *event) { (void)event; @@ -954,218 +828,6 @@ void I_GetEvent(void) // In order to make wheels act like buttons, we have to set their state to Up. // This is because wheel messages don't have an up/down state. gamekeydown[KEY_MOUSEWHEELDOWN] = gamekeydown[KEY_MOUSEWHEELUP] = 0; - -#if 0 - SDL_Event inputEvent; - static SDL_bool sdlquit = SDL_FALSE; //Alam: once, just once - event_t event; - - if (!graphics_started) - return; - - memset(&inputEvent, 0x00, sizeof(inputEvent)); - while (SDL_PollEvent(&inputEvent)) - { - memset(&event,0x00,sizeof (event_t)); - switch (inputEvent.type) - { - case SDL_ACTIVEEVENT: - if (inputEvent.active.state & (SDL_APPACTIVE|SDL_APPINPUTFOCUS)) - { - // pause music when alt-tab - if (inputEvent.active.gain /*&& !paused */) - { - static SDL_bool firsttimeonmouse = SDL_TRUE; - if (!firsttimeonmouse) - { - if (cv_usemouse.value) I_StartupMouse(); - } - else firsttimeonmouse = SDL_FALSE; - //if (!netgame && !con_destlines) paused = false; - if (gamestate == GS_LEVEL) - if (!paused) I_ResumeSong(0); //resume it - } - else /*if (!paused)*/ - { - if (!disable_mouse) - SDLforceUngrabMouse(); - if (!netgame && gamestate == GS_LEVEL) paused = true; - memset(gamekeydown, 0, NUMKEYS); - //S_PauseSound(); - if (gamestate == GS_LEVEL) - I_PauseSong(0); //pause it - } - } - if (MOUSE_MENU) - { - SDLdoUngrabMouse(); - break; - } - if ((SDL_APPMOUSEFOCUS&inputEvent.active.state) && USE_MOUSEINPUT && inputEvent.active.gain) - HalfWarpMouse(realwidth, realheight); - break; - case SDL_KEYDOWN: - case SDL_KEYUP: - /// \todo inputEvent.key.which? - if (inputEvent.type == SDL_KEYUP) - event.type = ev_keyup; - else if (inputEvent.type == SDL_KEYDOWN) - event.type = ev_keydown; - else break; - event.data1 = SDLatekey(inputEvent.key.keysym.sym); - if (event.data1) D_PostEvent(&event); - break; - case SDL_MOUSEMOTION: - /// \todo inputEvent.motion.which - if (MOUSE_MENU) - { - SDLdoUngrabMouse(); - break; - } - //if (USE_MOUSEINPUT) TODO SDL2 stub - { - // If the event is from warping the pointer back to middle - // of the screen then ignore it. - if ((inputEvent.motion.x == realwidth/2) && - (inputEvent.motion.y == realheight/2)) - { - break; - } - else - { - event.data2 = +inputEvent.motion.xrel; - event.data3 = -inputEvent.motion.yrel; - } - event.type = ev_mouse; - D_PostEvent(&event); - // Warp the pointer back to the middle of the window - // or we cannot move any further if it's at a border. - if ((inputEvent.motion.x < (realwidth/2 )-(realwidth/4 )) || - (inputEvent.motion.y < (realheight/2)-(realheight/4)) || - (inputEvent.motion.x > (realwidth/2 )+(realwidth/4 )) || - (inputEvent.motion.y > (realheight/2)+(realheight/4) ) ) - { - //if (SDL_GRAB_ON == SDL_WM_GrabInput(SDL_GRAB_QUERY) || !mousegrabok) - HalfWarpMouse(realwidth, realheight); - } - } - break; - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - /// \todo inputEvent.button.which - if (USE_MOUSEINPUT) - { - if (inputEvent.type == SDL_MOUSEBUTTONUP) - event.type = ev_keyup; - else if (inputEvent.type == SDL_MOUSEBUTTONDOWN) - event.type = ev_keydown; - else break; - if (inputEvent.button.button==SDL_BUTTON_WHEELUP || inputEvent.button.button==SDL_BUTTON_WHEELDOWN) - { - if (inputEvent.type == SDL_MOUSEBUTTONUP) - event.data1 = 0; //Alam: dumb! this could be a real button with some mice - else - event.data1 = KEY_MOUSEWHEELUP + inputEvent.button.button - SDL_BUTTON_WHEELUP; - } - else if (inputEvent.button.button == SDL_BUTTON_MIDDLE) - event.data1 = KEY_MOUSE1+2; - else if (inputEvent.button.button == SDL_BUTTON_RIGHT) - event.data1 = KEY_MOUSE1+1; - else if (inputEvent.button.button <= MOUSEBUTTONS) - event.data1 = KEY_MOUSE1 + inputEvent.button.button - SDL_BUTTON_LEFT; - if (event.data1) D_PostEvent(&event); - } - break; - case SDL_JOYAXISMOTION: - inputEvent.jaxis.which++; - inputEvent.jaxis.axis++; - event.data1 = event.data2 = event.data3 = INT32_MAX; - if (cv_usejoystick.value == inputEvent.jaxis.which) - { - event.type = ev_joystick; - } - else if (cv_usejoystick.value == inputEvent.jaxis.which) - { - event.type = ev_joystick2; - } - else break; - //axis - if (inputEvent.jaxis.axis > JOYAXISSET*2) - break; - //vaule - if (inputEvent.jaxis.axis%2) - { - event.data1 = inputEvent.jaxis.axis / 2; - event.data2 = SDLJoyAxis(inputEvent.jaxis.value, event.type); - } - else - { - inputEvent.jaxis.axis--; - event.data1 = inputEvent.jaxis.axis / 2; - event.data3 = SDLJoyAxis(inputEvent.jaxis.value, event.type); - } - D_PostEvent(&event); - break; - case SDL_JOYBALLMOTION: - case SDL_JOYHATMOTION: - break; //NONE - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: - inputEvent.jbutton.which++; - if (cv_usejoystick.value == inputEvent.jbutton.which) - event.data1 = KEY_JOY1; - else if (cv_usejoystick.value == inputEvent.jbutton.which) - event.data1 = KEY_2JOY1; - else break; - if (inputEvent.type == SDL_JOYBUTTONUP) - event.type = ev_keyup; - else if (inputEvent.type == SDL_JOYBUTTONDOWN) - event.type = ev_keydown; - else break; - if (inputEvent.jbutton.button < JOYBUTTONS) - event.data1 += inputEvent.jbutton.button; - else - break; - SDLJoyRemap(&event); - if (event.type != ev_console) D_PostEvent(&event); - break; - case SDL_QUIT: - if (!sdlquit) - { - sdlquit = SDL_TRUE; - M_QuitResponse('y'); - } - break; -#if defined(RPC_NO_WINDOWS_H) - case SDL_SYSWMEVENT: - MainWndproc(inputEvent.syswm.msg->hwnd, - inputEvent.syswm.msg->msg, - inputEvent.syswm.msg->wParam, - inputEvent.syswm.msg->lParam); - break; -#endif - case SDL_VIDEORESIZE: - if (gamestate == GS_LEVEL || gamestate == GS_TITLESCREEN || gamestate == GS_EVALUATION) - setmodeneeded = VID_GetModeForSize(inputEvent.resize.w,inputEvent.resize.h)+1; - if (render_soft == rendermode) - { - SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW); - if (vidSurface) SDL_SetColors(vidSurface, localPalette, 0, 256); - } - else - SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW); - if (!vidSurface) - I_Error("Could not reset vidmode: %s\n",SDL_GetError()); - break; - case SDL_VIDEOEXPOSE: - exposevideo = SDL_TRUE; - break; - default: - break; - } - } - //reset wheel like in win32, I don't understand it but works -#endif } void I_StartupMouse(void) @@ -1494,11 +1156,6 @@ void VID_PrepareModeList(void) #endif } -static inline void SDLESSet(void) -{ - SDL2STUB(); -} - INT32 VID_SetMode(INT32 modeNum) { SDLdoUngrabMouse(); @@ -1550,6 +1207,12 @@ INT32 VID_SetMode(INT32 modeNum) static SDL_bool Impl_CreateWindow(SDL_bool fullscreen) { int flags = 0; + + if (rendermode == render_none) // dedicated + { + return SDL_TRUE; // Monster Iestyn -- not sure if it really matters what we return here tbh + } + if (window != NULL) { return SDL_FALSE; @@ -1568,38 +1231,43 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen) #ifdef HWRENDER if (rendermode == render_opengl) { - window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - realwidth, realheight, flags | SDL_WINDOW_OPENGL); - if (window != NULL) - { - sdlglcontext = SDL_GL_CreateContext(window); - if (sdlglcontext == NULL) - { - SDL_DestroyWindow(window); - I_Error("Failed to create a GL context: %s\n", SDL_GetError()); - } - else - { - SDL_GL_MakeCurrent(window, sdlglcontext); - } - } - else return SDL_FALSE; + flags |= SDL_WINDOW_OPENGL; } #endif + + // Create a window + window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + realwidth, realheight, flags); + + if (window == NULL) + { + CONS_Printf(M_GetText("Couldn't create window: %s\n"), SDL_GetError()); + return SDL_FALSE; + } + + // Renderer-specific stuff +#ifdef HWRENDER + if (rendermode == render_opengl) + { + sdlglcontext = SDL_GL_CreateContext(window); + if (sdlglcontext == NULL) + { + SDL_DestroyWindow(window); + I_Error("Failed to create a GL context: %s\n", SDL_GetError()); + } + SDL_GL_MakeCurrent(window, sdlglcontext); + } + else +#endif if (rendermode == render_soft) { - window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - realwidth, realheight, flags); - if (window != NULL) + renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0)); + if (renderer == NULL) { - renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0)); - if (renderer != NULL) - { - SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT); - } - else return SDL_FALSE; + CONS_Printf(M_GetText("Couldn't create rendering context: %s\n"), SDL_GetError()); + return SDL_FALSE; } - else return SDL_FALSE; + SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT); } return SDL_TRUE; @@ -1620,7 +1288,7 @@ static void Impl_SetWindowIcon(void) { return; } - SDL2STUB(); + //SDL2STUB(); // Monster Iestyn: why is this stubbed? SDL_SetWindowIcon(window, icoSurface); } @@ -1718,7 +1386,6 @@ void I_StartupGraphics(void) borderlesswindow = M_CheckParm("-borderless"); //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2); - SDLESSet(); VID_Command_ModeList_f(); #ifdef HWRENDER if (M_CheckParm("-opengl") || rendermode == render_opengl) diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 4a46813c1..88bbadd20 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -220,7 +220,7 @@ static Mix_Chunk *ds2chunk(void *stream) break; default: // convert arbitrary hz to 44100. step = 0; - frac = ((UINT32)freq << FRACBITS) / 44100; + frac = ((UINT32)freq << FRACBITS) / 44100 + 1; //Add 1 to counter truncation. while (i < samples) { o = (INT16)(*s+0x80)<<8; // changed signedness and shift up to 16 bits diff --git a/src/sdl/ogl_sdl.h b/src/sdl/ogl_sdl.h index 7e144644c..2d6209f2b 100644 --- a/src/sdl/ogl_sdl.h +++ b/src/sdl/ogl_sdl.h @@ -24,7 +24,6 @@ boolean OglSdlSurface(INT32 w, INT32 h); void OglSdlFinishUpdate(boolean vidwait); -extern SDL_Window *window; extern SDL_Renderer *renderer; extern SDL_GLContext sdlglcontext; extern Uint16 realwidth; diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h index 7ac32f4b3..fea1e1648 100644 --- a/src/sdl/sdlmain.h +++ b/src/sdl/sdlmain.h @@ -71,4 +71,7 @@ void I_GetConsoleEvents(void); void SDLforceUngrabMouse(void); +// Needed for some WIN32 functions +extern SDL_Window *window; + #endif diff --git a/src/sdl12/i_system.c b/src/sdl12/i_system.c index 888a6a507..ed0db653d 100644 --- a/src/sdl12/i_system.c +++ b/src/sdl12/i_system.c @@ -2666,6 +2666,18 @@ INT32 I_PutEnv(char *variable) #endif } +INT32 I_ClipboardCopy(const char *data, size_t size) +{ + (void)data; + (void)size; + return -1; +} + +char *I_ClipboardPaste(void) +{ + return NULL; +} + /** \brief The isWadPathOk function \param path string path to check diff --git a/src/v_video.c b/src/v_video.c index 3cc6d195f..f6a966e67 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -774,43 +774,51 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) if (!screens[0]) return; - if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT) - { // Clear the entire screen, from dest to deststop. Yes, this really works. - memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp); - return; - } - - dest = screens[0] + y*dupy*vid.width + x*dupx; - deststop = screens[0] + vid.rowbytes * vid.height; - - if (w == BASEVIDWIDTH) - w = vid.width; - else - w *= dupx; - if (h == BASEVIDHEIGHT) - h = vid.height; - else - h *= dupy; - - if (x && y && x + w < vid.width && y + h < vid.height) + if (c & V_NOSCALESTART) { - // Center it if necessary - if (vid.width != BASEVIDWIDTH * dupx) - { - // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, - // so center this imaginary screen - if (c & V_SNAPTORIGHT) - dest += (vid.width - (BASEVIDWIDTH * dupx)); - else if (!(c & V_SNAPTOLEFT)) - dest += (vid.width - (BASEVIDWIDTH * dupx)) / 2; + dest = screens[0] + y*vid.width + x; + deststop = screens[0] + vid.rowbytes * vid.height; + } + else + { + if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT) + { // Clear the entire screen, from dest to deststop. Yes, this really works. + memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp); + return; } - if (vid.height != BASEVIDHEIGHT * dupy) + + dest = screens[0] + y*dupy*vid.width + x*dupx; + deststop = screens[0] + vid.rowbytes * vid.height; + + if (w == BASEVIDWIDTH) + w = vid.width; + else + w *= dupx; + if (h == BASEVIDHEIGHT) + h = vid.height; + else + h *= dupy; + + if (x && y && x + w < vid.width && y + h < vid.height) { - // same thing here - if (c & V_SNAPTOBOTTOM) - dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width; - else if (!(c & V_SNAPTOTOP)) - dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2; + // Center it if necessary + if (vid.width != BASEVIDWIDTH * dupx) + { + // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, + // so center this imaginary screen + if (c & V_SNAPTORIGHT) + dest += (vid.width - (BASEVIDWIDTH * dupx)); + else if (!(c & V_SNAPTOLEFT)) + dest += (vid.width - (BASEVIDWIDTH * dupx)) / 2; + } + if (vid.height != BASEVIDHEIGHT * dupy) + { + // same thing here + if (c & V_SNAPTOBOTTOM) + dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width; + else if (!(c & V_SNAPTOTOP)) + dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2; + } } } @@ -968,45 +976,38 @@ void V_DrawFadeScreen(void) } // Simple translucency with one color, over a set number of lines starting from the top. -void V_DrawFadeConsBack(INT32 plines, INT32 pcolor) +void V_DrawFadeConsBack(INT32 plines) { - UINT8 *deststop, *colormap, *buf; + UINT8 *deststop, *buf; #ifdef HWRENDER // not win32 only 19990829 by Kin if (rendermode != render_soft && rendermode != render_none) { UINT32 hwcolor; - switch (pcolor) + switch (cons_backcolor.value) { - case 0: hwcolor = 0xffffff00; break; //white - case 1: hwcolor = 0xff800000; break; //orange - case 2: hwcolor = 0x0000ff00; break; //blue - case 3: hwcolor = 0x00800000; break; //green - case 4: hwcolor = 0x80808000; break; //gray - case 5: hwcolor = 0xff000000; break; //red - default: hwcolor = 0x00800000; break; //green + case 0: hwcolor = 0xffffff00; break; // White + case 1: hwcolor = 0x80808000; break; // Gray + case 2: hwcolor = 0x40201000; break; // Brown + case 3: hwcolor = 0xff000000; break; // Red + case 4: hwcolor = 0xff800000; break; // Orange + case 5: hwcolor = 0x80800000; break; // Yellow + case 6: hwcolor = 0x00800000; break; // Green + case 7: hwcolor = 0x0000ff00; break; // Blue + case 8: hwcolor = 0x4080ff00; break; // Cyan + // Default green + default: hwcolor = 0x00800000; break; } HWR_DrawConsoleBack(hwcolor, plines); return; } #endif - switch (pcolor) - { - case 0: colormap = cwhitemap; break; - case 1: colormap = corangemap; break; - case 2: colormap = cbluemap; break; - case 3: colormap = cgreenmap; break; - case 4: colormap = cgraymap; break; - case 5: colormap = credmap; break; - default: colormap = cgreenmap; break; - } - // heavily simplified -- we don't need to know x or y position, // just the stop position deststop = screens[0] + vid.rowbytes * min(plines, vid.height); for (buf = screens[0]; buf < deststop; ++buf) - *buf = colormap[*buf]; + *buf = consolebgmap[*buf]; } // Gets string colormap, used for 0x80 color codes diff --git a/src/v_video.h b/src/v_video.h index 70255d0ef..353f84c1d 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -145,7 +145,7 @@ void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum); // fade down the screen buffer before drawing the menu over void V_DrawFadeScreen(void); -void V_DrawFadeConsBack(INT32 plines, INT32 pcolor); +void V_DrawFadeConsBack(INT32 plines); // draw a single character void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed); diff --git a/src/win32/Makefile.cfg b/src/win32/Makefile.cfg index f309f7db1..99b8bc9b2 100644 --- a/src/win32/Makefile.cfg +++ b/src/win32/Makefile.cfg @@ -85,13 +85,21 @@ endif OBJS=$(OBJDIR)/dx_error.o $(OBJDIR)/fabdxlib.o $(OBJDIR)/win_vid.o $(OBJDIR)/win_dll.o endif + +ZLIB_CFLAGS?=-I../libs/zlib +ifdef MINGW64 +ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz64 +else +ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz32 +endif + ifndef NOPNG ifndef PNG_CONFIG - PNG_CFLAGS?=-I../libs/libpng-src -I../libs/zlib + PNG_CFLAGS?=-I../libs/libpng-src ifdef MINGW64 - PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng64 -L../libs/zlib/win32 -lz64 + PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng64 else - PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng32 -L../libs/zlib/win32 -lz32 + PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng32 endif #MINGW64 endif #PNG_CONFIG endif #NOPNG diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index 0331080c8..80b89a6e6 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -3598,6 +3598,18 @@ INT32 I_PutEnv(char *variable) return putenv(variable); } +INT32 I_ClipboardCopy(const char *data, size_t size) +{ + (void)data; + (void)size; + return -1; +} + +const char *I_ClipboardPaste(void) +{ + return NULL; +} + typedef BOOL (WINAPI *p_IsProcessorFeaturePresent) (DWORD); const CPUInfoFlags *I_CPUInfo(void) diff --git a/src/win32ce/win_sys.c b/src/win32ce/win_sys.c index 88764ef73..3b6a47258 100644 --- a/src/win32ce/win_sys.c +++ b/src/win32ce/win_sys.c @@ -3470,6 +3470,18 @@ INT32 I_PutEnv(char *variable) return putenv(variable); } +INT32 I_ClipboardCopy(const char *data, size_t size) +{ + (void)data; + (void)size; + return -1; +} + +char *I_ClipboardPaste(void) +{ + return NULL; +} + typedef BOOL (WINAPI *MyFunc3) (DWORD); const CPUInfoFlags *I_CPUInfo(void)