diff --git a/engine/Makefile b/engine/Makefile index bd7b4e503..53b0b7b4f 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -554,6 +554,7 @@ SERVER_OBJS = \ sv_sql.o \ sv_mvd.o \ sv_ccmds.o \ + sv_cluster.o \ sv_rankin.o \ sv_chat.o \ sv_demo.o \ diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 740d06875..ee66ff19f 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -1802,7 +1802,9 @@ void CL_QTVPoll (void) int len; qboolean streamavailable = false; qboolean saidheader = false; +#ifndef NOBUITINMENUS menu_t *sourcesmenu = NULL; +#endif int sourcenum = 0; int streamid; @@ -1969,6 +1971,7 @@ void CL_QTVPoll (void) { streamid = atoi(colon); +#ifndef NOBUITINMENUS //now put it on a menu if (!sourcesmenu) { @@ -1983,6 +1986,7 @@ void CL_QTVPoll (void) MC_AddConsoleCommand(sourcesmenu, 42, 170, (sourcenum++)*8 + 32, va("%s (p%i, v%i)", srchost, numplayers, numviewers), va("qtvplay %i@%s\n", streamid, qtvhostname)); //else // FIXME: add error message here +#endif } //end of sourcelist entry diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 90079ac98..739020402 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -865,15 +865,6 @@ void CL_FinishMove (usercmd_t *cmd, int msecs, int pnum) cmd->impulse = 0; } -void CL_DrawPrydonCursor(void) -{ - if (cursor_active && cl_prydoncursor.ival > 0) - { - SCR_DrawCursor(cl_prydoncursor.ival); - V_StopPitchDrift (0); - } -} - void CL_UpdatePrydonCursor(usercmd_t *from, float cursor_screen[2], vec3_t cursor_start, vec3_t cursor_impact, int *entnum) { vec3_t cursor_end; @@ -1479,8 +1470,6 @@ qboolean CLQW_SendCmd (sizebuf_t *buf) MSG_WriteFloat(buf, cursor_impact[2]); MSG_WriteEntity(buf, cursor_entitynumber); } - else - cursor_active = false; MSG_WriteByte (buf, clc_move); @@ -1563,6 +1552,7 @@ void CL_SendCmd (double frametime, qboolean mainloop) if (cls.demoplayback != DPB_NONE || cls.netchan.remote_address.type == NA_INVALID) { + cursor_active = false; if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV) { extern cvar_t cl_splitscreen; @@ -1786,6 +1776,7 @@ void CL_SendCmd (double frametime, qboolean mainloop) msecs -= (double)msecstouse; return; } + cursor_active = false; switch (cls.protocol) { #ifdef NQPROT diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 4cf76b0a2..17378108e 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -146,6 +146,7 @@ cvar_t cl_gunangley = CVAR("cl_gunangley", "0"); cvar_t cl_gunanglez = CVAR("cl_gunanglez", "0"); cvar_t cl_sendguid = CVARD("cl_sendguid", "0", "Send a randomly generated 'globally unique' id to servers, which can be used by servers for score rankings and stuff. Different servers will see different guids. Delete the 'qkey' file in order to appear as a different user."); +cvar_t cl_downloads = CVARFD("cl_downloads", "1", CVAR_NOTFROMSERVER, "Allows you to block all automatic downloads."); cvar_t cl_download_csprogs = CVARFD("cl_download_csprogs", "1", CVAR_NOTFROMSERVER, "Download updated client gamecode if available. Warning: If you clear this to avoid downloading vm code, you should also clear cl_download_packages."); cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOTFROMSERVER, "Follow download redirection to download packages instead of individual files. 2 allows redirection only to named packages files. Also allows the server to send nearly arbitary download commands."); cvar_t cl_download_mapsrc = CVARD("cl_download_mapsrc", "", "Specifies an http location prefix for map downloads. EG: \"http://bigfoot.morphos-team.net/misc/quakemaps/\""); @@ -2454,7 +2455,7 @@ void CL_ConnectionlessPacket (void) netadr_t adr; char *data = MSG_ReadStringLine(); Con_TPrintf ("redirect to %s\n", data); - NET_StringToAdr(data, 27500, &adr); + NET_StringToAdr(data, PORT_QWSERVER, &adr); data = "\xff\xff\xff\xffgetchallenge\n"; connectinfo.istransfer = true; connectinfo.adr = adr; @@ -3186,7 +3187,7 @@ void CL_Download_f (void) return; } - CL_EnqueDownload(url, url, DLLF_IGNOREFAILED|DLLF_REQUIRED|DLLF_OVERWRITE|DLLF_VERBOSE); + CL_EnqueDownload(url, url, DLLF_USEREXPLICIT|DLLF_IGNOREFAILED|DLLF_REQUIRED|DLLF_OVERWRITE|DLLF_VERBOSE); } void CL_DownloadSize_f(void) @@ -3512,6 +3513,7 @@ void CL_Init (void) Cvar_Register (&r_drawflame, "Item effects"); + Cvar_Register (&cl_downloads, cl_controlgroup); Cvar_Register (&cl_download_csprogs, cl_controlgroup); Cvar_Register (&cl_download_redirection, cl_controlgroup); Cvar_Register (&cl_download_packages, cl_controlgroup); @@ -3617,7 +3619,7 @@ void CL_Init (void) Cmd_AddCommandD ("connect", CL_Connect_f, "connect scheme://address:port\nConnect to a server. Use a scheme of tcp:// or tls:// to connect via non-udp protocols." #if defined(NQPROT) || defined(Q2CLIENT) || defined(Q3CLIENT) - "\nDefault port is port 27500." + "\nDefault port is port "STRINGIFY(PORT_QWSERVER)"." #ifdef NQPROT " NQ:"STRINGIFY(PORT_NQSERVER)"." #endif @@ -4731,8 +4733,10 @@ void CL_StartCinematicOrMenu(void) { if (qrenderer > QR_NONE && !m_state) { +#ifndef NOBUITINMENUS if (!cls.state && !m_state && !*FS_GetGamedir(false)) M_Menu_Mods_f(); +#endif if (!cls.state && !m_state && cl_demoreel.ival) CL_NextDemo(); if (!cls.state && !m_state) diff --git a/engine/client/cl_master.h b/engine/client/cl_master.h index 79dbc7fa4..407c81e73 100644 --- a/engine/client/cl_master.h +++ b/engine/client/cl_master.h @@ -179,6 +179,7 @@ serverinfo_t *Master_InfoForNum (int num); unsigned int Master_TotalCount(void); unsigned int Master_NumPolled(void); void Master_SetupSockets(void); +void MasterInfo_Refresh(void); void Master_QueryServer(serverinfo_t *server); void MasterInfo_WriteServers(void); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index c16521a73..d706cdbfd 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -425,6 +425,7 @@ int CL_IsDownloading(const char *localname) //returns true if the download is going to be downloaded after the call. qboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned int flags) { + extern cvar_t cl_downloads; char *ext; downloadlist_t *dl; qboolean webdl = false; @@ -450,6 +451,13 @@ qboolean CL_EnqueDownload(const char *filename, const char *localname, unsigned return false; } + if (!(flags & DLLF_USEREXPLICIT) && !cl_downloads.ival) + { + if (flags & DLLF_VERBOSE) + Con_Printf("cl_downloads setting prevents download of \"%s\"\n", filename); + return false; + } + /*reject if it already failed*/ if (!(flags & DLLF_IGNOREFAILED)) { @@ -5819,8 +5827,7 @@ void CLQW_ParseServerMessage (void) } else if (cls.state == ca_connected) { - Host_EndGame ("Server disconnected\n" - "Server version may not be compatible"); + Host_EndGame ("Server disconnected\n"); } else Host_EndGame ("Server disconnected"); diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index dc5f72576..af22d0f2b 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -241,6 +241,8 @@ cvar_t show_speed_x = SCVAR("show_speed_x", "-1"); cvar_t show_speed_y = SCVAR("show_speed_y", "-9"); cvar_t scr_loadingrefresh = SCVAR("scr_loadingrefresh", "0"); +void *scr_curcursor; + extern char cl_screengroup[]; void CLSCR_Init(void) { @@ -261,6 +263,12 @@ void CLSCR_Init(void) Cvar_Register(&show_speed_x, cl_screengroup); Cvar_Register(&show_speed_y, cl_screengroup); Cvar_Register(&scr_neticontimeout, cl_screengroup); + + + memset(&key_customcursor, 0, sizeof(key_customcursor)); + scr_curcursor = NULL; + if (rf && rf->VID_SetCursor) + rf->VID_SetCursor(scr_curcursor); } /* @@ -612,33 +620,87 @@ void R_DrawTextField(int x, int y, int w, int h, const char *text, unsigned int SCR_DrawCenterString(&r, &p, font_default); } -void SCR_DrawCursor(int prydoncursornum) +qboolean SCR_HardwareCursorIsActive(void) { - extern cvar_t cl_cursor, cl_cursorbias, cl_cursorsize; + if (Key_MouseShouldBeFree()) + return !!scr_curcursor; + return false; +} +void SCR_DrawCursor(void) +{ + extern cvar_t cl_cursor, cl_cursorbiasx, cl_cursorbiasy, cl_cursorscale, cl_prydoncursor; mpic_t *p; + char *newc; + int prydoncursornum = 0; + extern qboolean cursor_active; + int cmod = kc_console; + void *oldcurs = NULL; - if (key_dest_absolutemouse & kdm_game) + if (cursor_active && cl_prydoncursor.ival > 0) + prydoncursornum = cl_prydoncursor.ival; + else if (!Key_MouseShouldBeFree()) + return; + + //choose the cursor based upon the module that has primary focus + if (key_dest_mask & key_dest_absolutemouse & (kdm_console|kdm_editor)) + cmod = kc_console; + else if ((key_dest_mask & key_dest_absolutemouse & kdm_menu)) { - //if the game is meant to be drawing a cursor, don't draw one over the top. - key_dest_absolutemouse &= ~kdm_game; - if (!Key_MouseShouldBeFree()) - { //unless something else wants a cursor too. - key_dest_absolutemouse |= kdm_game; - return; + if (m_state == m_menu_dat) + cmod = kc_menu; + else + cmod = kc_console; + } + else// if (key_dest_mask & key_dest_absolutemouse) + cmod = prydoncursornum?kc_console:kc_game; + + if (cmod == kc_console) + { + if (!*cl_cursor.string || prydoncursornum>1) + newc = va("gfx/prydoncursor%03i.lmp", prydoncursornum); + else + newc = cl_cursor.string; + if (strcmp(key_customcursor[kc_console].name, newc) || key_customcursor[kc_console].hotspot[0] != cl_cursorbiasx.value || key_customcursor[kc_console].hotspot[1] != cl_cursorbiasy.value || key_customcursor[kc_console].scale != cl_cursorscale.value) + { + key_customcursor[kc_console].dirty = true; + Q_strncpyz(key_customcursor[cmod].name, newc, sizeof(key_customcursor[cmod].name)); + key_customcursor[kc_console].hotspot[0] = cl_cursorbiasx.value; + key_customcursor[kc_console].hotspot[1] = cl_cursorbiasy.value; + key_customcursor[kc_console].scale = cl_cursorscale.value; } - key_dest_absolutemouse |= kdm_game; } - if (!*cl_cursor.string || prydoncursornum>1) - p = R2D_SafeCachePic(va("gfx/prydoncursor%03i.lmp", prydoncursornum)); - else - p = R2D_SafeCachePic(cl_cursor.string); + if (key_customcursor[cmod].dirty) + { + key_customcursor[cmod].dirty = false; + oldcurs = key_customcursor[cmod].handle; + if (rf->VID_CreateCursor) + { + key_customcursor[cmod].handle = rf->VID_CreateCursor(key_customcursor[cmod].name, key_customcursor[cmod].hotspot[0], key_customcursor[cmod].hotspot[1]); + if (!key_customcursor[cmod].handle) + key_customcursor[cmod].handle = rf->VID_CreateCursor("gfx/cursor.lmp", key_customcursor[cmod].hotspot[0], key_customcursor[cmod].hotspot[1]); //try the fallback + } + } + + if (scr_curcursor != key_customcursor[cmod].handle) + { + scr_curcursor = key_customcursor[cmod].handle; + rf->VID_SetCursor(scr_curcursor); + } + if (oldcurs) + rf->VID_DestroyCursor(oldcurs); + + if (scr_curcursor) + return; + //system doesn't support a hardware cursor, so try to draw a software one. + + p = R2D_SafeCachePic(key_customcursor[cmod].name); if (!p) p = R2D_SafeCachePic("gfx/cursor.lmp"); if (p) { R2D_ImageColours(1, 1, 1, 1); - R2D_Image(mousecursor_x-cl_cursorbias.value, mousecursor_y-cl_cursorbias.value, cl_cursorsize.value, cl_cursorsize.value, 0, 0, 1, 1, p); + R2D_Image(mousecursor_x-key_customcursor[cmod].hotspot[0], mousecursor_y-key_customcursor[cmod].hotspot[1], p->width*cl_cursorscale.value, p->height*cl_cursorscale.value, 0, 0, 1, 1, p); } else { @@ -1714,7 +1776,7 @@ void SCR_SetUpToDrawConsole (void) // Key_Dest_Add(kdm_console); scr_conlines = scr_con_current = vid.height * fullscreenpercent; } - else if (!Key_Dest_Has(kdm_menu) && (!Key_Dest_Has(~(kdm_console|kdm_game))) && SCR_GetLoadingStage() == LS_NONE && cls.state < ca_active && !Media_PlayingFullScreen() && !CSQC_UnconnectedOkay(false)) + else if (!Key_Dest_Has(kdm_menu) && (!Key_Dest_Has(~((!con_stayhidden.ival?kdm_console:0)|kdm_game))) && SCR_GetLoadingStage() == LS_NONE && cls.state < ca_active && !Media_PlayingFullScreen() && !CSQC_UnconnectedOkay(false)) { //go fullscreen if we're not doing anything #ifdef VM_UI @@ -1728,6 +1790,7 @@ void SCR_SetUpToDrawConsole (void) if (con_stayhidden.ival) { extern qboolean startuppending; + scr_conlines = 0; if (SCR_GetLoadingStage() == LS_NONE) { if (CL_TryingToConnect()) //if we're trying to connect, make sure there's a loading/connecting screen showing instead of forcing the menu visible @@ -2275,8 +2338,6 @@ void SCR_DrawTwoDimensional(int uimenu, qboolean nohud) SCR_DrawTurtle (); SCR_DrawPause (); SCR_ShowPics_Draw(); - - CL_DrawPrydonCursor(); } else { @@ -2304,8 +2365,7 @@ void SCR_DrawTwoDimensional(int uimenu, qboolean nohud) if (consolefocused) SCR_DrawConsole (false); - if (Key_MouseShouldBeFree()) - SCR_DrawCursor(0); + SCR_DrawCursor(); SCR_DrawSimMTouchCursor(); RSpeedEnd(RSPEED_2D); diff --git a/engine/client/client.h b/engine/client/client.h index bea3982e3..3f242ac53 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -504,8 +504,9 @@ typedef struct downloadlist_s { #define DLLF_IGNOREFAILED (1u<<4) // #define DLLF_NONGAME (1u<<5) //means the requested download filename+localname is gamedir explicit (so id1/foo.txt is distinct from qw/foo.txt) #define DLLF_TEMPORARY (1u<<6) //download it, but don't actually save it (DLLF_OVERWRITE doesn't actually overwrite, but does ignore any local files) +#define DLLF_USEREXPLICIT (1u<<7) //use explicitly requested it, ignore the cl_downloads cvar. -#define DLLF_BEGUN (1u<<7) //server has confirmed that the file exists, is readable, and we've opened a file. should not be set on new requests. +#define DLLF_BEGUN (1u<<8) //server has confirmed that the file exists, is readable, and we've opened a file. should not be set on new requests. struct downloadlist_s *next; } downloadlist_t; diff --git a/engine/client/console.c b/engine/client/console.c index 3be96d02b..1a007d7bc 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -462,7 +462,7 @@ void Con_ToggleConsole_f (void) } #endif - if (con_stayhidden.ival >= 2) + if (con_stayhidden.ival >= 3) return; //its hiding! Con_ToggleConsole_Force(); diff --git a/engine/client/image.c b/engine/client/image.c index 3b72d28b2..e182295a9 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -2608,7 +2608,7 @@ qbyte *Read32BitImageFile(qbyte *buf, int len, int *width, int *height, qboolean int w = LittleLong(((int*)buf)[0]); int h = LittleLong(((int*)buf)[1]); int i; - if (w >= 3 && h >= 4 && w*h+sizeof(int)*2 == com_filesize) + if (w >= 3 && h >= 4 && w*h+sizeof(int)*2 == len) { qboolean foundalpha = false; qbyte *in = (qbyte*)((int*)buf+2); diff --git a/engine/client/in_win.c b/engine/client/in_win.c index eb3731720..9abaea42d 100644 --- a/engine/client/in_win.c +++ b/engine/client/in_win.c @@ -538,10 +538,13 @@ void INS_UpdateGrabs(int fullscreen, int activeapp) grabmouse = false; //visiblity - if (grabmouse || (activeapp && mousecursor_x > 0 && mousecursor_y > 0 && mousecursor_x < vid.pixelwidth-1 && mousecursor_y < vid.pixelheight-1)) + if (!SCR_HardwareCursorIsActive() && (grabmouse || (activeapp && mousecursor_x > 0 && mousecursor_y > 0 && mousecursor_x < vid.pixelwidth-1 && mousecursor_y < vid.pixelheight-1))) INS_HideMouse(); else + { INS_ShowMouse(); + grabmouse = false; + } #ifdef HLCLIENT //halflife gamecode does its own mouse control... yes this is vile. @@ -1341,7 +1344,7 @@ potentially called multiple times per frame. */ void INS_Accumulate (void) { - static POINT current_pos; //static to avoid bugs in vista with largeaddressaware (this is fixed in win7). fixed exe base address prevents this from going above 2gb. + static POINT current_pos; //static to avoid bugs in vista(32) with largeaddressaware (this is fixed in win7). fixed exe base address prevents this from going above 2gb. if (mouseactive && !dinput) { diff --git a/engine/client/keys.c b/engine/client/keys.c index 84438fca5..b3014bd0d 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -43,6 +43,8 @@ unsigned int key_dest_mask; qboolean key_dest_console; unsigned int key_dest_absolutemouse; +struct key_cursor_s key_customcursor[kc_max]; + int key_count; // incremented every key event char *keybindings[K_MAX][KEY_MODIFIERSTATES]; @@ -1969,7 +1971,7 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) if (shift_down) { extern cvar_t con_stayhidden; - if (down && con_stayhidden.ival < 3) + if (down && con_stayhidden.ival < 2) { if (!Key_Dest_Has(kdm_console)) //don't toggle it when the console is already down. this allows typing blind to not care if its already active. Con_ToggleConsole_Force(); diff --git a/engine/client/keys.h b/engine/client/keys.h index 226624eed..12dc69903 100644 --- a/engine/client/keys.h +++ b/engine/client/keys.h @@ -192,6 +192,22 @@ extern int key_repeats[K_MAX]; extern int key_count; // incremented every key event extern int key_lastpress; +enum +{ + kc_game, + kc_menu, + kc_console, + kc_max +}; +extern struct key_cursor_s +{ + char name[MAX_QPATH]; + float hotspot[2]; + float scale; + qboolean dirty; + void *handle; +} key_customcursor[kc_max]; + extern unsigned char *chat_buffer; extern int chat_bufferpos; extern qboolean chat_team; diff --git a/engine/client/m_download.c b/engine/client/m_download.c index d425c8e25..57d44bc39 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -1,6 +1,6 @@ #include "quakedef.h" -#ifdef WEBCLIENT +#if defined(WEBCLIENT) && !defined(NOBUITINMENUS) #define DOWNLOADMENU #endif diff --git a/engine/client/m_items.c b/engine/client/m_items.c index eaecd209f..5fb014d6e 100644 --- a/engine/client/m_items.c +++ b/engine/client/m_items.c @@ -3,18 +3,6 @@ #include "quakedef.h" #include "shader.h" -int omousex; -int omousey; -qboolean mousemoved; -qboolean bindingactive; -extern cvar_t cl_cursor; -extern cvar_t cl_cursorsize; -extern cvar_t cl_cursorbias; -extern cvar_t m_preset_chosen; -menu_t *currentmenu; -menu_t *firstmenu; -menuoption_t *M_NextSelectableItem(menu_t *m, menuoption_t *old); - void Draw_TextBox (int x, int y, int width, int lines) { mpic_t *p; @@ -88,6 +76,20 @@ void Draw_TextBox (int x, int y, int width, int lines) R2D_ScalePic (cx, cy+8, 8, 8, p); } +#ifndef NOBUITINMENUS + +int omousex; +int omousey; +qboolean mousemoved; +qboolean bindingactive; +extern cvar_t cl_cursor; +extern cvar_t cl_cursorsize; +extern cvar_t cl_cursorbias; +extern cvar_t m_preset_chosen; +menu_t *currentmenu; +menu_t *firstmenu; +menuoption_t *M_NextSelectableItem(menu_t *m, menuoption_t *old); + void Draw_Hexen2BigFontString(int x, int y, const char *text) { int sx, sy; @@ -2283,3 +2285,4 @@ int MC_AddBulk(struct menu_s *menu, menuresel_t *resel, menubulk_t *bulk, int xs menu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, resel, xtextend + 8, selectedy); return y; } +#endif diff --git a/engine/client/m_master.c b/engine/client/m_master.c index 5f0757434..a9e2512db 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -1,6 +1,6 @@ #include "quakedef.h" -#ifdef CL_MASTER +#if defined(CL_MASTER) && !defined(NOBUITINMENUS) #include "cl_master.h" //filtering @@ -8,10 +8,10 @@ static cvar_t sb_hideempty = SCVARF("sb_hideempty", "0", CVAR_ARCHIVE); static cvar_t sb_hidenotempty = SCVARF("sb_hidenotempty", "0", CVAR_ARCHIVE); static cvar_t sb_hidefull = SCVARF("sb_hidefull", "0", CVAR_ARCHIVE); static cvar_t sb_hidedead = SCVARF("sb_hidedead", "1", CVAR_ARCHIVE); -cvar_t sb_hidequake2 = SCVARF("sb_hidequake2", "1", CVAR_ARCHIVE); -cvar_t sb_hidequake3 = SCVARF("sb_hidequake3", "1", CVAR_ARCHIVE); -cvar_t sb_hidenetquake = SCVARF("sb_hidenetquake", "1", CVAR_ARCHIVE); -cvar_t sb_hidequakeworld = SCVARF("sb_hidequakeworld","0", CVAR_ARCHIVE); +extern cvar_t sb_hidequake2; +extern cvar_t sb_hidequake3; +extern cvar_t sb_hidenetquake; +extern cvar_t sb_hidequakeworld; static cvar_t sb_showping = SCVARF("sb_showping", "1", CVAR_ARCHIVE); static cvar_t sb_showaddress = SCVARF("sb_showaddress", "0", CVAR_ARCHIVE); diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index b8e3736a8..d868ffe5c 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -579,7 +579,7 @@ void Media_Next_f (void) - +#ifndef NOMEDIAMENU void M_Menu_Media_f (void) { @@ -742,7 +742,14 @@ void M_Media_Key (int key) { int dir; if (key == K_ESCAPE) + { +#ifndef NOBUITINMENUS M_Menu_Main_f(); +#else + m_state = m_none; + Key_Dest_Remove(kdm_menu); +#endif + } else if (key == K_RIGHTARROW || key == K_LEFTARROW) { if (key == K_RIGHTARROW) @@ -1022,6 +1029,7 @@ void Media_LoadTrackNames (char *listname) } } } +#endif //safeprints only. char *Media_NextTrack(int musicchannelnum) @@ -1039,8 +1047,10 @@ char *Media_NextTrack(int musicchannelnum) if (!fakecdactive) Media_EndedTrack(); +#ifndef NOMEDIAMENU if (!loadedtracknames) Media_LoadTrackNames("sound/media.m3u"); +#endif if (!tracks && !fakecdactive) { *currenttrack.filename='\0'; @@ -1072,7 +1082,6 @@ char *Media_NextTrack(int musicchannelnum) - #undef dwFlags #undef lpFormat #undef lpData diff --git a/engine/client/m_multi.c b/engine/client/m_multi.c index d8381906e..fa37ab8a4 100644 --- a/engine/client/m_multi.c +++ b/engine/client/m_multi.c @@ -4,6 +4,8 @@ #include "winquake.h" #include "shader.h" +#ifndef NOBUITINMENUS + extern cvar_t maxclients; /* MULTIPLAYER MENU */ @@ -928,3 +930,4 @@ void M_Menu_Network_f (void) menu = M_Options_Title(&y, 0); MC_AddBulk(menu, &resel, bulk, 16, 200, y); } +#endif diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 88b141c96..578b64281 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -2,6 +2,99 @@ #include "quakedef.h" #include "winquake.h" + + +static const char *res4x3[] = +{ + "640x480", + "800x600", + "960x720", + "1024x768", + "1152x864", + "1280x960", + "1440x1080", + "1600x1200", +// "1792x1344", +// "1856x1392", + "1920x1440", + "2048x1536", + NULL +}; +static const char *res5x4[] = +{ + "1280x1024", + "1800x1440", + "2560x2048", + NULL +}; +static const char *res16x9[] = +{ + "856x480", + "1024x576", + "1280x720", + "1366x768", + "1600x900", + "1920x1080", + "2048x1152", + "2560x1440", + "3840x2160", + "4096x2304", + NULL +}; +static const char *res16x10[] = +{ + "1024x640", + "1152x720", + "1280x800", + "1440x900", + "1680x1050", + "1920x1200", + "2304x1440", + "2560x1600", + NULL +}; +#define ASPECT_RATIOS 4 +static const char **resaspects[ASPECT_RATIOS] = +{ + res4x3, + res5x4, + res16x9, + res16x10 +}; +#define ASPECT_LIST "4:3", "5:4", "16:9", "16:10", + +qboolean M_Vid_GetMode(int num, int *w, int *h) +{ + int i; + + for (i = 0; i < 4; i++) + { + const char **v = resaspects[i]; + while (*v && num) + { + v++; + num--; + } + if (*v) + { + const char *c = *v; + const char *s = strchr(c, 'x'); + if (s) + { + *w = atoi(c); + *h = atoi(s + 1); + return true; + } + return false; + } + } + return false; +} + + + +#ifndef NOBUITINMENUS + extern qboolean forcesaveprompt; extern cvar_t pr_debugger; @@ -554,6 +647,7 @@ const char *presetexec[] = "seta r_particlesystem null;" "seta r_particledesc \"\";" "seta r_part_classic_square 0;" + "seta r_part_classic_expgrav 10;" "seta r_stains 0;" "seta r_drawflat 1;" "seta r_nolerp 1;" @@ -617,6 +711,7 @@ const char *presetexec[] = "gl_texturemode nn;" //yup, we went there. "gl_texturemode2d n;" //yeah, 2d too. "r_part_classic_square 1;" //blocky baby! + "r_part_classic_expgrav 1;" //vanillaery "cl_sbar 1;" //its a style thing "sv_nqplayerphysics 1;" //gb wanted this "cl_demoreel 1;" //yup, arcadey @@ -630,6 +725,7 @@ const char *presetexec[] = "r_particledesc classic;" #endif "r_part_classic_square 0;" + "r_part_classic_expgrav 10;" //gives a slightly more dynamic feel to them "gl_load24bit 1;" "r_replacemodels \"md3 md2\";" "r_coronas 1;" @@ -1989,93 +2085,6 @@ void M_Menu_Singleplayer_Cheats_f (void) #define MULTIRENDERER // allow options for selecting renderer #endif -static const char *res4x3[] = -{ - "640x480", - "800x600", - "960x720", - "1024x768", - "1152x864", - "1280x960", - "1440x1080", - "1600x1200", -// "1792x1344", -// "1856x1392", - "1920x1440", - "2048x1536", - NULL -}; -static const char *res5x4[] = -{ - "1280x1024", - "1800x1440", - "2560x2048", - NULL -}; -static const char *res16x9[] = -{ - "856x480", - "1024x576", - "1280x720", - "1366x768", - "1600x900", - "1920x1080", - "2048x1152", - "2560x1440", - "3840x2160", - "4096x2304", - NULL -}; -static const char *res16x10[] = -{ - "1024x640", - "1152x720", - "1280x800", - "1440x900", - "1680x1050", - "1920x1200", - "2304x1440", - "2560x1600", - NULL -}; -#define ASPECT_RATIOS 4 -static const char **resaspects[ASPECT_RATIOS] = -{ - res4x3, - res5x4, - res16x9, - res16x10 -}; -#define ASPECT_LIST "4:3", "5:4", "16:9", "16:10", - -qboolean M_Vid_GetMode(int num, int *w, int *h) -{ - int i; - - for (i = 0; i < 4; i++) - { - const char **v = resaspects[i]; - while (*v && num) - { - v++; - num--; - } - if (*v) - { - const char *c = *v; - const char *s = strchr(c, 'x'); - if (s) - { - *w = atoi(c); - *h = atoi(s + 1); - return true; - } - return false; - } - } - return false; -} - typedef struct { menucombo_t *resmode; menuedit_t *width; @@ -2849,4 +2858,5 @@ void M_Menu_Mods_f (void) c->key = Mods_Key; menu->remove = Mods_Remove; } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/engine/client/m_script.c b/engine/client/m_script.c index f35b673d8..2423d2ae2 100644 --- a/engine/client/m_script.c +++ b/engine/client/m_script.c @@ -3,6 +3,8 @@ #include "quakedef.h" #include "shader.h" +#ifndef NOBUITINMENUS + int selectitem; menu_t *menu_script; @@ -436,3 +438,4 @@ void M_Script_Init(void) Cmd_AddCommand("menucomboi", M_MenuS_Comboi_f); Cmd_AddCommand("menucombos", M_MenuS_Combos_f); } +#endif diff --git a/engine/client/m_single.c b/engine/client/m_single.c index 4bcc1c13d..125957996 100644 --- a/engine/client/m_single.c +++ b/engine/client/m_single.c @@ -3,6 +3,7 @@ #include "quakedef.h" #include "winquake.h" #include "shader.h" +#ifndef NOBUITINMENUS #ifndef CLIENTONLY //============================================================================= /* LOAD/SAVE MENU */ @@ -811,5 +812,4 @@ void M_Menu_MediaFiles_f (void) ShowDemoMenu(menu, ""); } - - +#endif diff --git a/engine/client/menu.c b/engine/client/menu.c index 8661e9d1d..c46dc88ba 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -21,98 +21,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "winquake.h" #include "shader.h" -void M_Menu_Audio_f (void); -void M_Menu_Demos_f (void); -void M_Menu_Mods_f (void); -void M_Menu_ModelViewer_f(void); - m_state_t m_state; -extern menu_t *menu_script; - -qboolean m_recursiveDraw; - -void M_ConfigureNetSubsystem(void); - -cvar_t m_helpismedia = SCVAR("m_helpismedia", "0"); -cvar_t m_preset_chosen = CVARF("m_preset_chosen", "0", CVAR_ARCHIVE); - -//============================================================================= -/* Support Routines */ - -void M_Print (int cx, int cy, qbyte *str) -{ - Draw_AltFunString(cx + ((vid.width - 320)>>1), cy, str); -} -void M_PrintWhite (int cx, int cy, qbyte *str) -{ - Draw_FunString(cx + ((vid.width - 320)>>1), cy, str); -} void M_DrawScalePic (int x, int y, int w, int h, mpic_t *pic) { R2D_ScalePic (x + ((vid.width - 320)>>1), y, w, h, pic); } - -void M_BuildTranslationTable(int top, int bottom, unsigned int *translationTable) -{ - int j; - - int pc = Cvar_Get("cl_playerclass", "1", 0, "Hexen2")->value; - if (h2playertranslations && pc) - { - int i; - unsigned int color_offsets[5] = {2*14*256,0,1*14*256,2*14*256,2*14*256}; - unsigned char *colorA, *colorB, *sourceA, *sourceB; - colorA = h2playertranslations + 256 + color_offsets[pc-1]; - colorB = colorA + 256; - sourceA = colorB + (top * 256); - sourceB = colorB + (bottom * 256); - for(i=0;i<255;i++) - { - if (bottom > 0 && (colorB[i] != 255)) - translationTable[i] = d_8to24rgbtable[sourceB[i]] | 0xff000000; - else if (top > 0 && (colorA[i] != 255)) - translationTable[i] = d_8to24rgbtable[sourceA[i]] | 0xff000000; - else - translationTable[i] = d_8to24rgbtable[i] | 0xff000000; - } - } - else - { - for(j=0;j<255;j++) - { - if (j >= TOP_RANGE && j < TOP_RANGE + (1<<4)) - { - if (top >= 16) - { - *((unsigned char*)&translationTable[j]+0) = (((top&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[j&15]+0))>>8; - *((unsigned char*)&translationTable[j]+1) = (((top&0x00ff00)>> 8)**((unsigned char*)&d_8to24rgbtable[j&15]+1))>>8; - *((unsigned char*)&translationTable[j]+2) = (((top&0x0000ff)>> 0)**((unsigned char*)&d_8to24rgbtable[j&15]+2))>>8; - *((unsigned char*)&translationTable[j]+3) = 0xff; - } - else - translationTable[j] = d_8to24rgbtable[top<8?j-TOP_RANGE+(top<<4):(top<<4)+15-(j-TOP_RANGE)] | 0xff000000; - } - else if (j >= BOTTOM_RANGE && j < BOTTOM_RANGE + (1<<4)) - { - if (bottom >= 16) - { - *((unsigned char*)&translationTable[j]+0) = (((bottom&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[j&15]+0))>>8; - *((unsigned char*)&translationTable[j]+1) = (((bottom&0x00ff00)>> 8)**((unsigned char*)&d_8to24rgbtable[j&15]+1))>>8; - *((unsigned char*)&translationTable[j]+2) = (((bottom&0x0000ff)>> 0)**((unsigned char*)&d_8to24rgbtable[j&15]+2))>>8; - *((unsigned char*)&translationTable[j]+3) = 0xff; - } - else - translationTable[j] = d_8to24rgbtable[bottom<8?j-BOTTOM_RANGE+(bottom<<4):(bottom<<4)+15-(j-BOTTOM_RANGE)] | 0xff000000; - } - else - translationTable[j] = d_8to24rgbtable[j] | 0xff000000; - } - } - translationTable[255] = 0; //alpha -} - - void M_DrawTextBox (int x, int y, int width, int lines) { mpic_t *p; @@ -182,6 +96,151 @@ void M_DrawTextBox (int x, int y, int width, int lines) M_DrawScalePic (cx, cy+8, 8, 8, p); } +int M_FindKeysForBind (char *command, int *keylist, int total) +{ + int count; + int j; + int l; + char *b; + + for (count = 0; count < total; count++) + keylist[count] = -1; + l = strlen(command); + count = 0; + + for (j=0 ; j<256 ; j++) + { + b = keybindings[j][0]; + if (!b) + continue; + if (!strncmp (b, command, l) && (!b[l] || b[l] == ' ' || b[l] == ';')) + { + keylist[count] = j; + count++; + if (count == total) + break; + } + } + return count; +} +void M_FindKeysForCommand (int pnum, const char *command, int *twokeys) +{ + char prefix[5]; + + if (*command == '+' || *command == '-') + { + prefix[0] = *command; + prefix[1] = 0; + if (pnum != 0) + { + prefix[1] = 'p'; + prefix[2] = '0'+pnum; + prefix[3] = ' '; + prefix[4] = 0; + } + command++; + } + else + { + prefix[0] = 0; + if (pnum != 0) + { + prefix[0] = 'p'; + prefix[1] = '0'+pnum; + prefix[2] = ' '; + prefix[3] = 0; + } + } + M_FindKeysForBind(va("%s%s", prefix, command), twokeys, 2); +} + +#ifndef NOBUITINMENUS + +void M_Menu_Audio_f (void); +void M_Menu_Demos_f (void); +void M_Menu_Mods_f (void); +void M_Menu_ModelViewer_f(void); + +extern menu_t *menu_script; + +qboolean m_recursiveDraw; + +void M_ConfigureNetSubsystem(void); + +cvar_t m_helpismedia = SCVAR("m_helpismedia", "0"); +cvar_t m_preset_chosen = CVARF("m_preset_chosen", "0", CVAR_ARCHIVE); + +//============================================================================= +/* Support Routines */ + +void M_Print (int cx, int cy, qbyte *str) +{ + Draw_AltFunString(cx + ((vid.width - 320)>>1), cy, str); +} +void M_PrintWhite (int cx, int cy, qbyte *str) +{ + Draw_FunString(cx + ((vid.width - 320)>>1), cy, str); +} + +void M_BuildTranslationTable(int top, int bottom, unsigned int *translationTable) +{ + int j; + + int pc = Cvar_Get("cl_playerclass", "1", 0, "Hexen2")->value; + if (h2playertranslations && pc) + { + int i; + unsigned int color_offsets[5] = {2*14*256,0,1*14*256,2*14*256,2*14*256}; + unsigned char *colorA, *colorB, *sourceA, *sourceB; + colorA = h2playertranslations + 256 + color_offsets[pc-1]; + colorB = colorA + 256; + sourceA = colorB + (top * 256); + sourceB = colorB + (bottom * 256); + for(i=0;i<255;i++) + { + if (bottom > 0 && (colorB[i] != 255)) + translationTable[i] = d_8to24rgbtable[sourceB[i]] | 0xff000000; + else if (top > 0 && (colorA[i] != 255)) + translationTable[i] = d_8to24rgbtable[sourceA[i]] | 0xff000000; + else + translationTable[i] = d_8to24rgbtable[i] | 0xff000000; + } + } + else + { + for(j=0;j<255;j++) + { + if (j >= TOP_RANGE && j < TOP_RANGE + (1<<4)) + { + if (top >= 16) + { + *((unsigned char*)&translationTable[j]+0) = (((top&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[j&15]+0))>>8; + *((unsigned char*)&translationTable[j]+1) = (((top&0x00ff00)>> 8)**((unsigned char*)&d_8to24rgbtable[j&15]+1))>>8; + *((unsigned char*)&translationTable[j]+2) = (((top&0x0000ff)>> 0)**((unsigned char*)&d_8to24rgbtable[j&15]+2))>>8; + *((unsigned char*)&translationTable[j]+3) = 0xff; + } + else + translationTable[j] = d_8to24rgbtable[top<8?j-TOP_RANGE+(top<<4):(top<<4)+15-(j-TOP_RANGE)] | 0xff000000; + } + else if (j >= BOTTOM_RANGE && j < BOTTOM_RANGE + (1<<4)) + { + if (bottom >= 16) + { + *((unsigned char*)&translationTable[j]+0) = (((bottom&0xff0000)>>16)**((unsigned char*)&d_8to24rgbtable[j&15]+0))>>8; + *((unsigned char*)&translationTable[j]+1) = (((bottom&0x00ff00)>> 8)**((unsigned char*)&d_8to24rgbtable[j&15]+1))>>8; + *((unsigned char*)&translationTable[j]+2) = (((bottom&0x0000ff)>> 0)**((unsigned char*)&d_8to24rgbtable[j&15]+2))>>8; + *((unsigned char*)&translationTable[j]+3) = 0xff; + } + else + translationTable[j] = d_8to24rgbtable[bottom<8?j-BOTTOM_RANGE+(bottom<<4):(bottom<<4)+15-(j-BOTTOM_RANGE)] | 0xff000000; + } + else + translationTable[j] = d_8to24rgbtable[j] | 0xff000000; + } + } + translationTable[255] = 0; //alpha +} + //============================================================================= int m_save_demonum; @@ -464,65 +523,6 @@ void M_Menu_Keys_f (void) } } -int M_FindKeysForBind (char *command, int *keylist, int total) -{ - int count; - int j; - int l; - char *b; - - for (count = 0; count < total; count++) - keylist[count] = -1; - l = strlen(command); - count = 0; - - for (j=0 ; j<256 ; j++) - { - b = keybindings[j][0]; - if (!b) - continue; - if (!strncmp (b, command, l) && (!b[l] || b[l] == ' ' || b[l] == ';')) - { - keylist[count] = j; - count++; - if (count == total) - break; - } - } - return count; -} - -void M_FindKeysForCommand (int pnum, const char *command, int *twokeys) -{ - char prefix[5]; - - if (*command == '+' || *command == '-') - { - prefix[0] = *command; - prefix[1] = 0; - if (pnum != 0) - { - prefix[1] = 'p'; - prefix[2] = '0'+pnum; - prefix[3] = ' '; - prefix[4] = 0; - } - command++; - } - else - { - prefix[0] = 0; - if (pnum != 0) - { - prefix[0] = 'p'; - prefix[1] = '0'+pnum; - prefix[2] = ' '; - prefix[3] = 0; - } - } - M_FindKeysForBind(va("%s%s", prefix, command), twokeys, 2); -} - void M_UnbindCommand (const char *command) { int j; @@ -1076,7 +1076,9 @@ void M_Init_Internal (void) Cmd_AddCommand ("menu_keys", M_Menu_Keys_f); Cmd_AddCommand ("help", M_Menu_Help_f); Cmd_AddCommand ("menu_quit", M_Menu_Quit_f); +#ifndef NOMEDIAMENU Cmd_AddCommand ("menu_media", M_Menu_Media_f); +#endif Cmd_AddCommand ("menu_mediafiles", M_Menu_MediaFiles_f); Cmd_AddCommand ("menu_mods", M_Menu_Mods_f); Cmd_AddCommand ("modelviewer", M_Menu_ModelViewer_f); @@ -1146,7 +1148,9 @@ void M_DeInit_Internal (void) Cmd_RemoveCommand ("menu_keys"); Cmd_RemoveCommand ("help"); Cmd_RemoveCommand ("menu_quit"); +#ifndef NOMEDIAMENU Cmd_RemoveCommand ("menu_media"); +#endif Cmd_RemoveCommand ("menu_mediafiles"); #ifdef CL_MASTER @@ -1180,7 +1184,7 @@ void M_DeInit_Internal (void) Cmd_RemoveCommand ("menu_download"); - Cmd_RemoveCommand ("menu_main"); //I've moved main to last because that way tab give us main and not quit. + Cmd_RemoveCommand ("menu_main"); //I've moved main to last because that way tab gives us main and not quit. Cmd_RemoveCommand ("quickconnect"); } @@ -1228,6 +1232,31 @@ void M_Init (void) M_Reinit(); } +//end builtin-menu code. +#else +void M_Init_Internal (void){} +void M_DeInit_Internal (void){} +void M_Shutdown(qboolean total) +{ +#ifdef MENU_DAT + MP_Shutdown(); +#endif +} +void M_Reinit(void) +{ +#ifdef MENU_DAT + if (!MP_Init()) +#endif + { + CSQC_UnconnectedInit(); + } +} +void M_Init (void) +{ + Media_Init(); + M_Reinit(); +} +#endif void M_Draw (int uimenu) @@ -1241,10 +1270,13 @@ void M_Draw (int uimenu) #endif } +#ifndef NOBUITINMENUS if (m_state != m_complex) { M_RemoveAllMenus(); } +#endif + if (!Key_Dest_Has(kdm_menu)) { m_state = m_none; @@ -1254,6 +1286,7 @@ void M_Draw (int uimenu) if (m_state == m_none || m_state == m_menu_dat) return; +#ifndef NOBUITINMENUS if ((!menu_script || scr_con_current) && !m_recursiveDraw) { extern menu_t *firstmenu; @@ -1266,6 +1299,7 @@ void M_Draw (int uimenu) { m_recursiveDraw = false; } +#endif R2D_ImageColours(1, 1, 1, 1); @@ -1274,17 +1308,22 @@ void M_Draw (int uimenu) case m_none: break; +#ifndef NOBUITINMENUS case m_help: M_Help_Draw (); break; - case m_media: - M_Media_Draw (); - break; - case m_complex: M_Complex_Draw (); break; +#endif + +#ifndef NOMEDIAMENU + case m_media: + M_Media_Draw (); + break; +#endif + #ifdef PLUGINS case m_plugin: Plug_Menu_Event (0, (int)(realtime*1000)); @@ -1306,18 +1345,22 @@ void M_Keydown (int key, int unicode) case m_none: Key_Dest_Remove(kdm_menu); return; - +#ifndef NOBUITINMENUS case m_help: M_Help_Key (key); return; - case m_media: - M_Media_Key (key); - return; - case m_complex: M_Complex_Key (key, unicode); return; +#endif + +#ifndef NOMEDIAMENU + case m_media: + M_Media_Key (key); + return; +#endif + #ifdef PLUGINS case m_plugin: Plug_Menu_Event (1, key); diff --git a/engine/client/menu.h b/engine/client/menu.h index 6b503e9b6..43b1d7346 100644 --- a/engine/client/menu.h +++ b/engine/client/menu.h @@ -96,7 +96,13 @@ void M_SomeInitialisationFunctionCalledAtStartup(void) } */ +typedef enum {m_none, m_complex, m_help, m_media, m_plugin, m_menu_dat} m_state_t; +extern m_state_t m_state; +void M_DrawTextBox (int x, int y, int width, int lines); +#define NOMEDIAMENU + +#ifndef NOBUITINMENUS // // menus @@ -110,16 +116,12 @@ void M_Draw (int uimenu); void M_ToggleMenu_f (void); void M_Menu_Mods_f (void); //used at startup if the current gamedirs look dodgy. mpic_t *M_CachePic (char *path); -void M_DrawTextBox (int x, int y, int width, int lines); void M_Menu_Quit_f (void); void M_Menu_Prompt (void (*callback)(void *, int), void *ctx, char *m1, char *m2, char *m3, char *optionyes, char *optionno, char *optioncancel); struct menu_s; -typedef enum {m_none, m_complex, m_help, m_media, m_plugin, m_menu_dat} m_state_t; -extern m_state_t m_state; - typedef enum { mt_childwindow, mt_button, @@ -401,6 +403,7 @@ void M_Menu_Search_f (void); void M_Menu_ServerList_f (void); void M_Menu_Media_f (void); +/* void M_Main_Draw (void); void M_SinglePlayer_Draw (void); void M_Load_Draw (void); @@ -416,7 +419,6 @@ void M_LanConfig_Draw (void); void M_GameOptions_Draw (void); void M_Search_Draw (void); void M_ServerList_Draw (void); -void M_Media_Draw (void); void M_Main_Key (int key); void M_SinglePlayer_Key (int key); @@ -433,7 +435,7 @@ void M_LanConfig_Key (int key); void M_GameOptions_Key (int key); void M_Search_Key (int key); void M_ServerList_Key (int key); -void M_Media_Key (int key); +*/ void MasterInfo_Refresh(void); void M_DrawServers(void); @@ -447,9 +449,27 @@ void M_PrintWhite (int cx, int cy, qbyte *str); void M_DrawScalePic (int x, int y, int w, int h, mpic_t *pic); -void M_FindKeysForCommand (int pnum, const char *command, int *twokeys); void M_UnbindCommand (const char *command); +#else +//no builtin menu code. +//stubs +#define M_Menu_Prompt(cb,ctx,m1,m2,m3,optionyes,optionno,optioncancel) (cb)(ctx,-1) +#define M_ToggleMenu_f() Cbuf_AddText("togglemenu\n",RESTRICT_LOCAL) +//#define M_Shutdown(t) MP_Shutdown() + +void M_Init (void); +void M_Reinit(void); +void M_Shutdown(qboolean total); +void M_Keydown (int key, int unicode); +void M_Keyup (int key, int unicode); +void M_Draw (int uimenu); +#endif +void M_FindKeysForCommand (int pnum, const char *command, int *twokeys); + +void M_Media_Draw (void); +void M_Media_Key (int key); + void MP_CvarChanged(cvar_t *var); qboolean MP_Init (void); void MP_Shutdown (void); diff --git a/engine/client/merged.h b/engine/client/merged.h index abe9f1eca..4478e291f 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -326,6 +326,11 @@ typedef struct rendererinfo_s { void (*VID_DeInit) (void); void (*VID_SwapBuffers) (void); //force a buffer swap, regardless of what's displayed. qboolean (*VID_ApplyGammaRamps) (unsigned short *ramps); + + void *(*VID_CreateCursor) (char *filename, int hotx, int hoty); //may be null, stub returns null + qboolean (*VID_SetCursor) (void *cursor); //may be null + void (*VID_DestroyCursor) (void *cursor); //may be null + void (*VID_SetWindowCaption) (char *msg); char *(*VID_GetRGBInfo) (int prepad, int *truevidwidth, int *truevidheight); diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 1c0c32203..2f4df1776 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -36,6 +36,10 @@ typedef int SOCKET; cvar_t slist_cacheinfo = SCVAR("slist_cacheinfo", "0"); //this proves dangerous, memory wise. cvar_t slist_writeserverstxt = SCVAR("slist_writeservers", "0"); +cvar_t sb_hidequake2 = SCVARF("sb_hidequake2", "1", CVAR_ARCHIVE); +cvar_t sb_hidequake3 = SCVARF("sb_hidequake3", "1", CVAR_ARCHIVE); +cvar_t sb_hidenetquake = SCVARF("sb_hidenetquake", "1", CVAR_ARCHIVE); +cvar_t sb_hidequakeworld = SCVARF("sb_hidequakeworld","0", CVAR_ARCHIVE); void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad); void CL_QueryServers(void); @@ -1586,7 +1590,7 @@ void MasterInfo_Refresh(void) // Master_AddMaster("masterserver.exhale.de:27000", MT_MASTERUDP, MP_QW, "team exhale"); Master_AddMaster("qwmaster.fodquake.net:27000", MT_MASTERUDP, MP_QW, "Fodquake master server."); Master_AddMaster("qwmaster.ocrana.de:27000", MT_MASTERUDP, MP_QW, "Ocrana2 master server."); - Master_AddMaster("255.255.255.255:27500", MT_BCAST, MP_QW, "Nearby QuakeWorld UDP servers."); + Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_QWSERVER),MT_BCAST, MP_QW, "Nearby QuakeWorld UDP servers."); } // if (q1servers) //nq master servers @@ -1602,8 +1606,8 @@ void MasterInfo_Refresh(void) //Master_AddMaster("[2001:41d0:2:1628::4450]:27950", MT_MASTERUDP, MP_DP, "DarkPlaces Master 4"); // dpmaster.div0.qc.to (admin: divVerent) //Master_AddMaster("[2604:180::4ac:98c1]:27950", MT_MASTERUDP, MP_DP, "DarkPlaces Master 4"); // dpmaster.deathmask.net (Willis) #endif - Master_AddMaster("255.255.255.255:26000", MT_BCAST, MP_NQ, "Nearby Quake1 servers"); - Master_AddMaster("255.255.255.255:26000", MT_BCAST, MP_DP, "Nearby DarkPlaces servers"); + Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_NQSERVER), MT_BCAST, MP_NQ, "Nearby Quake1 servers"); + Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_NQSERVER), MT_BCAST, MP_DP, "Nearby DarkPlaces servers"); } // if (q2servers) //q2 @@ -1616,7 +1620,7 @@ void MasterInfo_Refresh(void) // Master_AddMaster("masterserver.exhale.de:27900", MT_MASTERUDP, MP_Q2, "team exhale"); Master_AddMaster("255.255.255.255:27910", MT_BCAST, MP_Q2, "Nearby Quake2 UDP servers."); #ifdef USEIPX - Master_AddMaster("00000000:ffffffffffff:27910", MT_BCAST, MP_Q2, "Nearby Quake2 IPX servers."); + Master_AddMaster("00000000:ffffffffffff:"STRINGIFY(PORT_Q2SERVER), MT_BCAST, MP_Q2, "Nearby Quake2 IPX servers."); #endif } @@ -1626,7 +1630,7 @@ void MasterInfo_Refresh(void) Master_AddMaster("master.quake3arena.com:27950", MT_MASTERUDP, MP_Q3, "Quake3 master server."); // Master_AddMaster("masterserver.exhale.de:27950", MT_MASTERUDP, MP_Q3, "team exhale"); //Master_AddMaster("master3.quake3arena.com:27950", MT_MASTERUDP, MP_Q3, "Quake3 master3 server."); - Master_AddMaster("255.255.255.255:27960", MT_BCAST, MP_Q3, "Nearby Quake3 UDP servers."); + Master_AddMaster("255.255.255.255:"STRINGIFY(PORT_Q3SERVER), MT_BCAST, MP_Q3, "Nearby Quake3 UDP servers."); } } diff --git a/engine/client/p_classic.c b/engine/client/p_classic.c index 693cdd781..5a44f9f3f 100644 --- a/engine/client/p_classic.c +++ b/engine/client/p_classic.c @@ -74,7 +74,7 @@ typedef struct cparticle_s #define ABSOLUTE_MAX_PARTICLES 8192 static int r_numparticles; static cparticle_t *particles, *active_particles, *free_particles; -extern cvar_t r_part_density; +extern cvar_t r_part_density, r_part_classic_expgrav; extern qbyte default_quakepal[]; /*for ramps more than anything else*/ static int ramp1[8] = {0x6f, 0x6d, 0x6b, 0x69, 0x67, 0x65, 0x63, 0x61}; @@ -501,7 +501,7 @@ static void PClassic_DrawParticles(void) p->rgb = qpal(ramp1[(int) p->ramp]); for (i = 0; i < 3; i++) p->vel[i] += p->vel[i] * dvel; - p->vel[2] -= grav*10; + p->vel[2] -= grav*r_part_classic_expgrav.value; break; case pt_explode2: p->ramp += time3; @@ -511,7 +511,7 @@ static void PClassic_DrawParticles(void) p->rgb = qpal(ramp2[(int) p->ramp]); for (i = 0; i < 3; i++) p->vel[i] -= p->vel[i] * frametime; - p->vel[2] -= grav*10; + p->vel[2] -= grav*r_part_classic_expgrav.value; break; case pt_blob: for (i = 0; i < 3; i++) diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 6eff9176b..043c57f41 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -2429,6 +2429,17 @@ static void QCBUILTIN PF_cl_setcursormode (pubprogfuncs_t *prinst, struct global key_dest_absolutemouse |= world->keydestmask; else key_dest_absolutemouse &= ~world->keydestmask; + + if (prinst->callargc>1) + { + struct key_cursor_s *m = &key_customcursor[(world->keydestmask==kdm_game)?kc_game:kc_menu]; + Q_strncpyz(m->name, PR_GetStringOfs(prinst, OFS_PARM1), sizeof(m->name)); + m->hotspot[0] = (prinst->callargc>2)?G_FLOAT(OFS_PARM2+0):0; + m->hotspot[1] = (prinst->callargc>2)?G_FLOAT(OFS_PARM2+1):0; + m->scale = (prinst->callargc>2)?G_FLOAT(OFS_PARM2+2):0; + if (m->scale <= 0) + m->scale = 1; + } } //get the input commands, and stuff them into some globals. @@ -4644,7 +4655,7 @@ static struct { {"stringtokeynum", PF_cl_stringtokeynum, 341}, // #341 float(string keyname) stringtokeynum (EXT_CSQC) {"getkeybind", PF_cl_getkeybind, 342}, // #342 string(float keynum) getkeybind (EXT_CSQC) - {"setcursormode", PF_cl_setcursormode, 343}, // #343 This is a DP extension + {"setcursormode", PF_cl_setcursormode, 343}, // #343 This is originally a DP extension {"getmousepos", PF_cl_getmousepos, 344}, // #344 This is a DP extension {"getinputstate", PF_cs_getinputstate, 345}, // #345 float(float framenum) getinputstate (EXT_CSQC) diff --git a/engine/client/r_part.c b/engine/client/r_part.c index 5721c4f34..2005aad2c 100644 --- a/engine/client/r_part.c +++ b/engine/client/r_part.c @@ -149,6 +149,7 @@ cvar_t r_part_sparks_textured = CVAR("r_part_sparks_textured", "1"); cvar_t r_part_beams = CVAR("r_part_beams", "1"); cvar_t r_part_contentswitch = CVARFD("r_part_contentswitch", "1", CVAR_ARCHIVE, "Enable particle effects to change based on content (ex. water)."); cvar_t r_part_density = CVARF("r_part_density", "1", CVAR_ARCHIVE); +cvar_t r_part_classic_expgrav = CVARFD("r_part_classic_expgrav", "10", CVAR_ARCHIVE, "Scaler for how fast classic explosion particles should accelerate due to gravity. 1 for like vanilla, 10 for like zquake."); particleengine_t *pe; @@ -174,6 +175,7 @@ void P_InitParticleSystem(void) Cvar_Register(&r_part_beams, particlecvargroupname); Cvar_Register(&r_part_contentswitch, particlecvargroupname); Cvar_Register(&r_part_density, particlecvargroupname); + Cvar_Register(&r_part_classic_expgrav, particlecvargroupname); Cvar_Register (&gl_part_flame, particlecvargroupname); diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 98bebf1c3..b07eca948 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -52,8 +52,9 @@ cvar_t _windowed_mouse = CVARF ("_windowed_mouse","1", cvar_t con_ocranaleds = CVAR ("con_ocranaleds", "2"); cvar_t cl_cursor = CVAR ("cl_cursor", ""); -cvar_t cl_cursorsize = CVAR ("cl_cursorsize", "32"); -cvar_t cl_cursorbias = CVAR ("cl_cursorbias", "4"); +cvar_t cl_cursorscale = CVAR ("cl_cursor_scale", "0.2"); +cvar_t cl_cursorbiasx = CVAR ("cl_cursor_bias_x", "7.5"); +cvar_t cl_cursorbiasy = CVAR ("cl_cursor_bias_y", "0.8"); cvar_t gl_nocolors = CVARF ("gl_nocolors", "0", CVAR_ARCHIVE); cvar_t gl_part_flame = CVARFD ("gl_part_flame", "1", CVAR_ARCHIVE, "Enable particle emitting from models. Mainly used for torch and flame effects."); @@ -77,6 +78,7 @@ cvar_t r_drawflat = CVARF ("r_drawflat", "0", CVAR_ARCHIVE | CVAR_SEMICHEAT | CVAR_RENDERERCALLBACK | CVAR_SHADERSYSTEM); cvar_t r_wireframe = CVARF ("r_wireframe", "0", CVAR_CHEAT); +cvar_t r_wireframe_smooth = CVAR ("r_wireframe_smooth", "0"); cvar_t r_refract_fbo = CVARD ("r_refract_fbo", "1", "Use an fbo for refraction. If 0, just renders as a portal and uses a copy of the current framebuffer."); cvar_t gl_miptexLevel = CVAR ("gl_miptexLevel", "0"); cvar_t r_drawviewmodel = CVARF ("r_drawviewmodel", "1", CVAR_ARCHIVE); @@ -168,8 +170,8 @@ cvar_t scr_conalpha = CVARC ("scr_conalpha", "0.7", cvar_t scr_consize = CVAR ("scr_consize", "0.5"); cvar_t scr_conspeed = CVAR ("scr_conspeed", "2000"); // 10 - 170 -cvar_t scr_fov = CVARFDC("fov", "108", - CVAR_ARCHIVE, "field of vision, 1-170 degrees, standard fov is 90, nquake.", +cvar_t scr_fov = CVARFDC("fov", "90", + CVAR_ARCHIVE, "field of vision, 1-170 degrees, standard fov is 90, nquake defaults to 108.", SCR_Fov_Callback); cvar_t scr_printspeed = SCVAR ("scr_printspeed", "8"); cvar_t scr_showpause = SCVAR ("showpause", "1"); @@ -646,6 +648,7 @@ void Renderer_Init(void) Cvar_Register (&r_slimestyle, GRAPHICALNICETIES); Cvar_Register (&r_telestyle, GRAPHICALNICETIES); Cvar_Register (&r_wireframe, GRAPHICALNICETIES); + Cvar_Register (&r_wireframe_smooth, GRAPHICALNICETIES); Cvar_Register (&r_refract_fbo, GRAPHICALNICETIES); Cvar_Register (&r_stereo_separation, GRAPHICALNICETIES); Cvar_Register (&r_stereo_method, GRAPHICALNICETIES); @@ -659,8 +662,9 @@ void Renderer_Init(void) Cvar_Register (&scr_sshot_prefix, SCREENOPTIONS); Cvar_Register(&cl_cursor, SCREENOPTIONS); - Cvar_Register(&cl_cursorsize, SCREENOPTIONS); - Cvar_Register(&cl_cursorbias, SCREENOPTIONS); + Cvar_Register(&cl_cursorscale, SCREENOPTIONS); + Cvar_Register(&cl_cursorbiasx, SCREENOPTIONS); + Cvar_Register(&cl_cursorbiasy, SCREENOPTIONS); //screen @@ -821,6 +825,9 @@ rendererinfo_t dedicatedrendererinfo = { NULL, //VID_DeInit, NULL, //VID_SwapBuffers NULL, //VID_ApplyGammaRamps, + NULL, + NULL, + NULL, NULL, //set caption NULL, //VID_GetRGBInfo, diff --git a/engine/client/screen.h b/engine/client/screen.h index aa43f2b8c..abeddd5a1 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -63,7 +63,7 @@ void SCR_DrawNet (void); void SCR_DrawTurtle (void); void SCR_DrawPause (void); void SCR_VRectForPlayer(vrect_t *vrect, int pnum); //returns a region for the player's view -void SCR_DrawCursor(int prydoncursornum); +qboolean SCR_HardwareCursorIsActive(void); void CLSCR_Init(void); //basically so I can register a few friendly cvars. diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index a410da2b1..48d20d2b4 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -726,7 +726,7 @@ DWORD CrashExceptionHandler (qboolean iswatchdog, DWORD exceptionCode, LPEXCEPTI #ifdef _MSC_VER if (MessageBoxA(0, stacklog, "KABOOM!", MB_ICONSTOP|MB_YESNO) != IDYES) { - if (pIsDebuggerPresent ()) + if (pIsDebuggerPresent && pIsDebuggerPresent ()) { //its possible someone attached a debugger while we were showing that message return EXCEPTION_CONTINUE_SEARCH; @@ -3312,14 +3312,14 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin SV_Init (&parms); - delay = SV_Frame()*1000; + delay = SV_Frame(); while (1) { if (!isDedicated) Sys_Error("Dedicated was cleared"); NET_Sleep(delay, false); - delay = SV_Frame()*1000; + delay = SV_Frame(); } return TRUE; } diff --git a/engine/client/vid_headless.c b/engine/client/vid_headless.c index 264d0bc21..0c0c4657e 100644 --- a/engine/client/vid_headless.c +++ b/engine/client/vid_headless.c @@ -254,6 +254,9 @@ rendererinfo_t headlessrenderer = Headless_VID_DeInit, Headless_VID_SwapBuffers, Headless_VID_ApplyGammaRamps, + NULL, + NULL, + NULL, Headless_VID_SetWindowCaption, Headless_VID_GetRGBInfo, Headless_SCR_UpdateScreen, diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 5a3015e36..2d76afa6b 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -84,6 +84,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define NO_JPEG #define NO_ZLIB #define NO_OGG + #else + #define AVAIL_OPENAL + #define AVAIL_FREETYPE #endif #define AVAIL_OGGVORBIS @@ -96,9 +99,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define AVAIL_OGGVORBIS #endif - #define AVAIL_OPENAL - #define AVAIL_FREETYPE - #if !defined(NO_DIRECTX) && !defined(NODIRECTX) && defined(_WIN32) #define AVAIL_DINPUT #define AVAIL_DDRAW @@ -106,11 +106,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define AVAIL_D3D #endif +#ifndef MINIMAL #if defined(_WIN32) && !defined(FTE_SDL) && !defined(WINRT) #define HAVE_WINSSPI //built in component, checks against windows' root ca database and revocations etc. #elif defined(__linux__) || defined(__CYGWIN__) #define HAVE_GNUTLS //currently disabled as it does not validate the server's certificate, beware the mitm attack. #endif +#endif #if defined(HAVE_WINSSPI) || defined(HAVE_GNUTLS) #define HAVE_SSL #endif @@ -168,7 +170,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef AVAIL_JPEGLIB //no jpeg support #undef AVAIL_PNGLIB //no png support - #undef USE_MADLIB //no internal mp3 playing #define NOMEDIA //NO playing of avis/cins/roqs #define SPRMODELS //quake1 sprite models @@ -216,7 +217,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define Q2BSPS //quake 2 bsp support #define Q3BSPS //quake 3 bsp support #define TERRAIN //heightmap support - #define SV_MASTER //starts up a master server +// #define SV_MASTER //starts up a master server #define SVCHAT //serverside npc chatting. see sv_chat.c #define Q2SERVER //server can run a q2 game dll and switches to q2 network and everything else. #define Q2CLIENT //client can connect to q2 servers @@ -268,6 +269,20 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif +#ifdef QUAKETC + #define NOBUITINMENUS //kill engine menus (should be replaced with ewither csqc or menuqc) + #undef Q2CLIENT //not useful + #undef Q2SERVER //not useful + #undef Q3CLIENT //not useful + #undef Q3SERVER //not useful + #undef HLCLIENT //not useful + #undef HLSERVER //not useful + #undef VM_Q1 //not useful + #undef VM_LUA //not useful + #undef HALFLIFEMODELS //yuck + #undef RUNTIMELIGHTING //presumably not useful +#endif + //#define QUAKESPYAPI //define this if you want the engine to be usable via gamespy/quakespy, which has been dead for a long time now. #ifdef FTE_TARGET_WEB @@ -287,7 +302,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef MAP_PROC //meh #undef HALFLIFEMODELS //blurgh #undef WEBSERVER //hah, yeah, right - #undef SUPPORT_ICE //kinda requires udp, but whatever + #undef SUPPORT_ICE //kinda requires udp, so not usable //extra features stripped to try to reduce memory footprints #undef RUNTIMELIGHTING //too slow anyway @@ -355,10 +370,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #endif -#if defined(RTLIGHTS) && !defined(GLQUAKE) && !defined(D3D9QUAKE) - #undef RTLIGHTS -#endif - #ifndef _WIN32 #undef QTERM //not supported - FIXME: move to native plugin #endif diff --git a/engine/common/common.c b/engine/common/common.c index cf05d2057..260cea3d5 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -96,6 +96,25 @@ cvar_t com_modname = CVARD("com_modname", "", "dpmaster information"); cvar_t com_parseutf8 = CVARD("com_parseutf8", "0", "Interpret console messages/playernames/etc as UTF-8. Requires special fonts. -1=iso 8859-1. 0=quakeascii(chat uses high chars). 1=utf8, revert to ascii on decode errors. 2=utf8 ignoring errors"); //1 parse. 2 parse, but stop parsing that string if a char was malformed. cvar_t com_highlightcolor = CVARD("com_highlightcolor", STRINGIFY(COLOR_RED), "ANSI colour to be used for highlighted text, used when com_parseutf8 is active."); cvar_t com_nogamedirnativecode = CVARFD("com_nogamedirnativecode", "1", CVAR_NOTFROMSERVER, FULLENGINENAME" blocks all downloads of files with a .dll or .so extension, however other engines (eg: ezquake and fodquake) do not - this omission can be used to trigger remote exploits in any engine (including "FULLENGINENAME"which is later run from the same gamedir.\nQuake2, Quake3(when debugging), and KTX typically run native gamecode from within gamedirs, so if you wish to run any of these games you will need to ensure this cvar is changed to 0, as well as ensure that you don't run unsafe clients.\n"); +#ifdef FTE_TARGET_WEB +cvar_t sys_platform = CVAR("sys_platform", "web"); +#elif defined(NACL) +cvar_t sys_platform = CVAR("sys_platform", "nacl"); +#elif defined(ANDROID) +cvar_t sys_platform = CVAR("sys_platform", "android"); +#elif defined(FTE_SDL) +cvar_t sys_platform = CVAR("sys_platform", "sdl"); +#elif defined(_WIN64) +cvar_t sys_platform = CVAR("sys_platform", "win64"); +#elif defined(_WIN32) +cvar_t sys_platform = CVAR("sys_platform", "win32"); +#elif defined(__linux__) +cvar_t sys_platform = CVAR("sys_platform", "linux"); +#elif defined(__APPLE__) +cvar_t sys_platform = CVAR("sys_platform", "mac"); +#else +cvar_t sys_platform = CVAR("sys_platform", ""); +#endif qboolean com_modified; // set true if using non-id files @@ -4540,6 +4559,7 @@ void COM_Init (void) Cmd_AddCommand ("errorme", COM_ErrorMe_f); COM_InitFilesystem (); + Cvar_Register (&sys_platform, "Gamecode"); Cvar_Register (®istered, "Copy protection"); Cvar_Register (&gameversion, "Gamecode"); Cvar_Register (&gameversion_min, "Gamecode"); diff --git a/engine/common/common.h b/engine/common/common.h index f4660c49f..69a8b9dd9 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -113,6 +113,7 @@ void InsertLinkAfter (link_t *l, link_t *after); // FIXME: remove this mess! #define STRUCT_FROM_LINK(l,t,m) ((t *)((qbyte *)l - (qbyte*)&(((t *)0)->m))) +#define FOR_EACH_LINK(l,node) for (l = node.next ; l != &node ; l = l->next) //============================================================================ #ifndef NULL diff --git a/engine/common/fs.c b/engine/common/fs.c index 0529eb511..e6a46ff4b 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -2135,10 +2135,11 @@ char *FS_GetGamedir(qboolean publicpathonly) //returns the commandline arguments required to duplicate the fs details char *FS_GetManifestArgs(void) { + char *homearg = com_homepathenabled?"-usehome ":"-nohome "; if (fs_manifest->updatefile) - return va("-manifest %s -basedir %s", fs_manifest->updatefile, com_gamepath); + return va("%s-manifest %s -basedir %s -outputdebugstring", homearg, fs_manifest->updatefile, com_gamepath); - return va("-game %s -basedir %s", pubgamedirfile, com_gamepath); + return va("%s-game %s -basedir %s -outputdebugstring", homearg, pubgamedirfile, com_gamepath); } //given a 'c:/foo/bar/' path, will extract 'bar'. @@ -3361,6 +3362,7 @@ static void FS_PackageDownloaded(struct dl_download *dl) } Sys_remove (fspdl_temppath); + fs_restarts++; FS_ChangeGame(fs_manifest, true); FS_BeginNextPackageDownload(); diff --git a/engine/common/log.c b/engine/common/log.c index e34f6e8cc..6faf93e4a 100644 --- a/engine/common/log.c +++ b/engine/common/log.c @@ -13,8 +13,8 @@ cvar_t log_enable[LOG_TYPES] = { CVARF("log_enable", "0", CVAR_NOTFROMSERVER), CVARF("log_enable_rcon", "1", CVAR_NOTFROMSERVER) }; cvar_t log_name[LOG_TYPES] = { CVARFC("log_name", "", CVAR_NOTFROMSERVER, Log_Name_Callback), - CVARFC("log_name_players", "", CVAR_NOTFROMSERVER, Log_Name_Callback), - CVARFC("log_name_rcon", "", CVAR_NOTFROMSERVER, Log_Name_Callback)}; + CVARFC("log_name_players", "players", CVAR_NOTFROMSERVER, Log_Name_Callback), + CVARFC("log_name_rcon", "rcon", CVAR_NOTFROMSERVER, Log_Name_Callback)}; cvar_t log_dir = CVARFC("log_dir", "", CVAR_NOTFROMSERVER, Log_Dir_Callback); cvar_t log_readable = CVARFD("log_readable", "7", CVAR_NOTFROMSERVER, "Bitfield describing what to convert/strip. If 0, exact byte representation will be used.\n&1: Dequakify text.\n&2: Strip special markup.\n&4: Strip ansi control codes."); cvar_t log_developer = CVARF("log_developer", "0", CVAR_NOTFROMSERVER); diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index b503475a3..0fbaaf105 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -5307,18 +5307,21 @@ void IPX_CloseSocket (int socket) qboolean NET_Sleep(float seconds, qboolean stdinissocket) { #ifdef HAVE_PACKET - struct timeval timeout; + struct timeval timeout; fd_set fdset; - int maxfd; + qintptr_t maxfd = -1; int con, sock; unsigned int usec; FD_ZERO(&fdset); if (stdinissocket) - FD_SET(0, &fdset); //stdin tends to be socket 0 + { + sock = 0; //stdin tends to be socket/filehandle 0 in unix + FD_SET(sock, &fdset); + maxfd = sock; + } - maxfd = 0; if (svs.sockets) for (con = 0; con < MAX_CONNECTIONS; con++) { @@ -5348,7 +5351,7 @@ qboolean NET_Sleep(float seconds, qboolean stdinissocket) usec += 1000; //slight extra delay, to ensure we don't wake up with nothing to do. timeout.tv_sec = usec/(1000*1000); timeout.tv_usec = usec; - if (!maxfd) + if (maxfd == -1) Sys_Sleep(seconds); else select(maxfd+1, &fdset, NULL, NULL, &timeout); @@ -5449,7 +5452,7 @@ void SVNET_AddPort_f(void) { svs.sockets = FTENET_CreateCollection(true); #ifndef SERVERONLY - FTENET_AddToCollection(svs.sockets, "SVLoopback", "27500", NA_LOOPBACK, true); + FTENET_AddToCollection(svs.sockets, "SVLoopback", STRINGIFY(PORT_QWSERVER), NA_LOOPBACK, true); #endif } @@ -5606,7 +5609,7 @@ void SV_Port_Callback(struct cvar_s *var, char *oldvalue) { FTENET_AddToCollection(svs.sockets, var->name, var->string, NA_IP, true); } -cvar_t sv_port_ipv4 = CVARC("sv_port", "27500", SV_Port_Callback); +cvar_t sv_port_ipv4 = CVARC("sv_port", STRINGIFY(PORT_QWSERVER), SV_Port_Callback); #endif #ifdef IPPROTO_IPV6 void SV_PortIPv6_Callback(struct cvar_s *var, char *oldvalue) diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 4f7081d82..c216efe32 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -320,6 +320,8 @@ enum clustercmdops_e //string message ccmd_acceptserver, //serverid + ccmd_lostplayer, //player dropped/timed out + //long plid ccmd_takeplayer, //master->server, saying to allocate a slot for a player. //long plid //long fromsvid (0=no reply needed) @@ -347,8 +349,13 @@ enum clustercmdops_e //byte statcount //float stats[statcount] ccmd_serveraddress, //server->master, contains a few net addresses - //string address[] + //string addresses[] //byte 0 + ccmd_stringcmd, + //string dest (black = broadcast to all) + //string source (player name) + //string cmd (type of event, handled by receiving server/forwarded to client) + //string msg (extra info, like the typed text) }; diff --git a/engine/common/sys_win_threads.c b/engine/common/sys_win_threads.c index 9a9ada612..1d76d9fda 100644 --- a/engine/common/sys_win_threads.c +++ b/engine/common/sys_win_threads.c @@ -24,7 +24,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "winquake.h" #include -#if (defined(_DEBUG) || defined(DEBUG)) && !defined(SERVERONLY) +#if (defined(_DEBUG) || defined(DEBUG)) #define CATCHCRASH LONG CALLBACK nonmsvc_CrashExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo); @@ -346,7 +346,7 @@ pubsubserver_t *Sys_ForkServer(void) GetModuleFileName(NULL, exename, sizeof(exename)); GetCurrentDirectory(sizeof(curdir), curdir); - Q_snprintfz(cmdline, sizeof(cmdline), "foo -clusterslave %s", FS_GetManifestArgs()); //fixme: include which manifest is in use, so configs get set up the same. + Q_snprintfz(cmdline, sizeof(cmdline), "foo -noreset -clusterslave %s", FS_GetManifestArgs()); //fixme: include which manifest is in use, so configs get set up the same. memset(&startinfo, 0, sizeof(startinfo)); startinfo.cb = sizeof(startinfo); diff --git a/engine/d3d/d3d11_shader.c b/engine/d3d/d3d11_shader.c index 092e102fa..c5320a5d7 100644 --- a/engine/d3d/d3d11_shader.c +++ b/engine/d3d/d3d11_shader.c @@ -565,6 +565,7 @@ qboolean D3D11Shader_Init(unsigned int flevel) sh_config.pCreateProgram = D3D11Shader_CreateProgram; sh_config.pProgAutoFields = NULL; + sh_config.texture_non_power_of_two = true; sh_config.tex_env_combine = 1; sh_config.nv_tex_env_combine4 = 1; sh_config.env_add = 1; diff --git a/engine/d3d/d3d_shader.c b/engine/d3d/d3d_shader.c index 6978ef555..0eee23d2d 100644 --- a/engine/d3d/d3d_shader.c +++ b/engine/d3d/d3d_shader.c @@ -377,6 +377,7 @@ void D3D9Shader_Init(void) sh_config.pCreateProgram = D3D9Shader_CreateProgram; sh_config.pProgAutoFields = D3D9Shader_ProgAutoFields; + sh_config.texture_non_power_of_two = 0; sh_config.tex_env_combine = 1; sh_config.nv_tex_env_combine4 = 1; sh_config.env_add = 1; diff --git a/engine/d3d/vid_d3d.c b/engine/d3d/vid_d3d.c index 25e367a81..f6d0c7090 100644 --- a/engine/d3d/vid_d3d.c +++ b/engine/d3d/vid_d3d.c @@ -1257,6 +1257,9 @@ rendererinfo_t d3d9rendererinfo = D3D9_VID_DeInit, D3D9_VID_SwapBuffers, D3D9_VID_ApplyGammaRamps, + NULL, + NULL, + NULL, D3D9_VID_SetWindowCaption, D3D9_VID_GetRGBInfo, diff --git a/engine/d3d/vid_d3d11.c b/engine/d3d/vid_d3d11.c index e0bea2b10..869e9bff5 100644 --- a/engine/d3d/vid_d3d11.c +++ b/engine/d3d/vid_d3d11.c @@ -1447,6 +1447,9 @@ rendererinfo_t d3d11rendererinfo = D3D11_VID_DeInit, D3D11_PresentOrCrash, D3D11_VID_ApplyGammaRamps, + NULL, + NULL, + NULL, D3D11_VID_SetWindowCaption, D3D11_VID_GetRGBInfo, diff --git a/engine/dotnet2005/ftequake.vcproj b/engine/dotnet2005/ftequake.vcproj index 8c6e7017c..37c486a4f 100644 --- a/engine/dotnet2005/ftequake.vcproj +++ b/engine/dotnet2005/ftequake.vcproj @@ -1280,7 +1280,9 @@ AdditionalIncludeDirectories="../libs/speex,..\client,../libs/freetype2/include,../common,../server,../gl,../sw,../qclib,../libs,../libs/dxsdk7/include" PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;SERVERONLY;MULTITHREAD" MinimalRebuild="true" + BasicRuntimeChecks="3" RuntimeLibrary="0" + EnableFunctionLevelLinking="true" FloatingPointModel="2" UsePrecompiledHeader="2" PrecompiledHeaderThrough="quakedef.h" @@ -3022,6 +3024,10 @@ /> + + @@ -27232,6 +27238,13 @@ + + + passes[0].shaderbits | SBITS_MISC_NODEPTHTEST); + BE_SendPassBlendDepthMask((shaderstate.curshader->passes[0].shaderbits & ~SBITS_BLEND_BITS) | SBITS_SRCBLEND_SRC_ALPHA | SBITS_DSTBLEND_ONE_MINUS_SRC_ALPHA | SBITS_MISC_NODEPTHTEST); BE_EnableShaderAttributes((1u<ptMinTrackSize.y = 200 + ((windowrect.bottom - windowrect.top) - (clientrect.bottom - clientrect.top)); } return 0; - case WM_SIZE: + case WM_SIZE: vid.isminimized = (wParam==SIZE_MINIMIZED); if (!vid_initializing) { VID_UpdateWindowStatus (hWnd); Cvar_ForceCallback(&vid_conautoscale); } - break; + break; - case WM_CLOSE: + case WM_CLOSE: if (!vid_initializing) if (MessageBox (mainwindow, "Are you sure you want to quit?", "Confirm Exit", MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION) == IDYES) @@ -2072,7 +2168,7 @@ LONG WINAPI GLMainWndProc ( Cbuf_AddText("\nquit\n", RESTRICT_LOCAL); } - break; + break; case WM_ACTIVATE: // fActive = LOWORD(wParam); @@ -2089,25 +2185,41 @@ LONG WINAPI GLMainWndProc ( break; - case WM_DESTROY: - { + case WM_DESTROY: if (dibwindow) DestroyWindow (dibwindow); - } - break; - - case MM_MCINOTIFY: - lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); + break; + case WM_SETCURSOR: + //only use a custom cursor if the cursor is inside the client area + switch(lParam&0xffff) + { + case 0: + break; + case HTCLIENT: + if (hCustomCursor) //custom cursor enabled + SetCursor(hCustomCursor); + else //fallback on an arrow cursor, just so we have something visible at startup or so + SetCursor(hArrowCursor); + lRet = TRUE; + break; + default: + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } break; - default: - /* pass all unhandled messages to DefWindowProc */ - lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); - break; - } + case MM_MCINOTIFY: + lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); + break; - /* return 1 if handled message, 0 if not */ - return lRet; + default: + /* pass all unhandled messages to DefWindowProc */ + lRet = DefWindowProc (hWnd, uMsg, wParam, lParam); + break; + } + + /* return 1 if handled message, 0 if not */ + return lRet; } @@ -2176,6 +2288,11 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) memset(&devmode, 0, sizeof(devmode)); hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1)); + hArrowCursor = LoadCursor (NULL,IDC_ARROW); + + rf->VID_CreateCursor = WIN_CreateCursor; + rf->VID_DestroyCursor = WIN_DestroyCursor; + rf->VID_SetCursor = WIN_SetCursor; /* Register the frame class */ wc.style = CS_OWNDC; @@ -2184,7 +2301,7 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) wc.cbWndExtra = 0; wc.hInstance = global_hInstance; wc.hIcon = hIcon; - wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.hCursor = hArrowCursor; wc.hbrBackground = NULL; wc.lpszMenuName = 0; wc.lpszClassName = WINDOW_CLASS_NAME; diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 30f1919d3..3f68350f1 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -626,6 +626,7 @@ typedef struct unsigned int minver; //lowest glsl version usable unsigned int maxver; //highest glsl version usable + qboolean texture_non_power_of_two; qboolean tex_env_combine; qboolean nv_tex_env_combine4; qboolean env_add; diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index b660e9e92..b4f7598f0 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -829,7 +829,7 @@ void GenericMenu(WPARAM wParam) break; case IDM_ABOUT: - MessageBox(NULL, "FTE QuakeC Compiler\nWritten by Forethough Entertainment.\n\nIt has a few cool features, like a semi-useful IDE.\n\nSupports:\nPrecompiler (with macros)\nArrays\n+= / -= / *= / /= operations.\nSwitch statements\nfor loops\nLots of optimisations.", "About", 0); + MessageBox(NULL, "FTE QuakeC Compiler ("__DATE__" "__TIME__")\nWritten by Forethought Entertainment.\n\nIt has a few cool features, like a semi-useful IDE.\n\nSupports:\nPrecompiler (with macros)\nArrays\n+= / -= / *= / /= operations.\nSwitch statements\nfor loops\nLots of optimisations.", "About", 0); break; case IDM_CASCADE: diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index c4b2e2884..f299eafaf 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -144,6 +144,7 @@ struct { func_t UserInfo_Changed; func_t localinfoChanged; + func_t ParseClusterEvent; //FTE_SV_CLUSTER func_t ParseClientCommand; //KRIMZON_SV_PARSECLIENTCOMMAND func_t ParseConnectionlessPacket; //FTE_QC_SENDPACKET @@ -773,6 +774,7 @@ void PR_LoadGlabalStruct(void) SpectatorDisconnect = PR_FindFunction(svprogfuncs, "SpectatorDisconnect", PR_ANY); SpectatorThink = PR_FindFunction(svprogfuncs, "SpectatorThink", PR_ANY); + gfuncs.ParseClusterEvent = PR_FindFunction(svprogfuncs, "SV_ParseClusterEvent", PR_ANY); gfuncs.ParseClientCommand = PR_FindFunction(svprogfuncs, "SV_ParseClientCommand", PR_ANY); gfuncs.ParseConnectionlessPacket = PR_FindFunction(svprogfuncs, "SV_ParseConnectionlessPacket", PR_ANY); @@ -1766,6 +1768,27 @@ qboolean PR_GameCodePacket(char *s) return G_FLOAT(OFS_RETURN); } +qboolean PR_ParseClusterEvent(char *dest, char *source, char *cmd, char *info) +{ + globalvars_t *pr_globals; + + if (svprogfuncs && gfuncs.ParseClusterEvent) + { + pr_globals = PR_globals(svprogfuncs, PR_CURRENT); + pr_global_struct->time = sv.world.physicstime; + pr_global_struct->self = 0; + + G_INT(OFS_PARM0) = (int)PR_TempString(svprogfuncs, dest); + G_INT(OFS_PARM1) = (int)PR_TempString(svprogfuncs, source); + G_INT(OFS_PARM2) = (int)PR_TempString(svprogfuncs, cmd); + G_INT(OFS_PARM3) = (int)PR_TempString(svprogfuncs, info); + PR_ExecuteProgram (svprogfuncs, gfuncs.ParseClusterEvent); + return true; + } + + return false; +} + qboolean PR_KrimzonParseCommand(char *s) { globalvars_t *pr_globals; @@ -8307,13 +8330,14 @@ int PF_ForceInfoKey_Internal(unsigned int entnum, const char *key, const char *v if (!strcmp(key, "*spectator")) svs.clients[entnum-1].spectator = !!atoi(value); -#ifdef SUBSERVERS if (!strcmp(key, "*transfer")) + { +#ifdef SUBSERVERS SSV_InitiatePlayerTransfer(&svs.clients[entnum-1], value); #else - if (*value) PF_ForceInfoKey_Internal(entnum, key, ""); #endif + } return 1; } @@ -8874,6 +8898,16 @@ static void QCBUILTIN PF_SendPacket(pubprogfuncs_t *prinst, struct globalvars_s NET_SendPacket(NS_SERVER, strlen(contents), contents, &to); } +static void QCBUILTIN PF_clusterevent(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ +#ifdef SUBSERVERS + const char *dest = PR_GetStringOfs(prinst, OFS_PARM0); + const char *src = PR_GetStringOfs(prinst, OFS_PARM1); + const char *cmd = PR_GetStringOfs(prinst, OFS_PARM2); + const char *info = PF_VarString(prinst, 13, pr_globals); + SSV_Send(dest, src, cmd, info); +#endif +} #define STUB ,NULL,true @@ -9368,6 +9402,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"hash_getcb", PF_hash_getcb, 0, 0, 0, 293, D("void(float table, void(string keyname, __variant val) callback, optional string name)", "For each item in the table that matches the name, call the callback. if name is omitted, will enumerate ALL keys."), true}, {"checkcommand", PF_checkcommand, 0, 0, 0, 294, D("float(string name)", "Checks to see if the supplied name is a valid command, cvar, or alias. Returns 0 if it does not exist.")}, {"argescape", PF_argescape, 0, 0, 0, 295, D("string(string s)", "Marks up a string so that it can be reliably tokenized as a single argument later.")}, + {"clusterevent", PF_clusterevent, 0, 0, 0, 296, D("void(string dest, string from, string cmd, string info)", "Only functions in mapcluster mode. Sends an event to whichever server the named player is on. The destination server can then dispatch the event to the client or handle it itself via the SV_ParseClusterEvent entrypoint. If dest is empty, the event is broadcast to ALL servers. If the named player can't be found, the event will be returned to this server with the cmd prefixed with 'error:'.")}, {"clearscene", PF_Fixme, 0, 0, 0, 300, D("void()", "Forgets all rentities, polygons, and temporary dlights. Resets all view properties to their default values.")},// (EXT_CSQC) @@ -9431,7 +9466,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"stringtokeynum_csqc", PF_Fixme,0, 0, 0, 341, D("float(string keyname)", "Looks up the key name in the same way that the bind command would, returning the keycode for that key.")},// (found in menuqc) {"getkeybind", PF_Fixme, 0, 0, 0, 342, D("string(float keynum)", "Finds the current binding for the given key (ignores modifiers like shift/alt/ctrl).")},// (EXT_CSQC) - {"setcursormode", PF_Fixme, 0, 0, 0, 343, D("void(float usecursor)", "Pass TRUE if you want the engine to release the mouse cursor (absolute input events + touchscreen mode). Pass FALSE if you want the engine to grab the cursor (relative input events + standard looking)")}, // #343 This is a DP extension + {"setcursormode", PF_Fixme, 0, 0, 0, 343, D("void(float usecursor, optional string cursorimage, optional vector hotspot, optional float scale)", "Pass TRUE if you want the engine to release the mouse cursor (absolute input events + touchscreen mode). Pass FALSE if you want the engine to grab the cursor (relative input events + standard looking). If the image name is specified, the engine will use that image for a cursor (use an empty string to clear it again), in a way that will not conflict with the console. Images specified this way will be hardware accelerated, if supported by the platform/port.")}, {"getmousepos", PF_Fixme, 0, 0, 0, 344, D("vector()", "Nasty convoluted DP extension. Typically returns deltas instead of positions. Use CSQC_InputEvent for such things in csqc mods.")}, // #344 This is a DP extension {"getinputstate", PF_Fixme, 0, 0, 0, 345, D("float(float inputsequencenum)", "Looks up an input frame from the log, setting the input_* globals accordingly.\nThe sequence number range used for prediction should normally be servercommandframe < sequence <= clientcommandframe.\nThe sequence equal to clientcommandframe will change between input frames.")},// (EXT_CSQC) @@ -10077,7 +10112,7 @@ typedef struct char *desc; - int value; + float value; char *valuestr; qboolean misc; } knowndef_t; @@ -10279,15 +10314,16 @@ void PR_DumpPlatform_f(void) {"SpectatorDisconnect", "noref void()", QW|NQ, "Called when a spectator disconnects from the game."}, {"SpectatorThink", "noref void()", QW|NQ, "Called each frame for each spectator."}, {"SV_ParseClientCommand", "noref void(string cmd)", QW|NQ, "Provides QC with a way to intercept 'cmd foo' commands from the client. Very handy. Self will be set to the sending client, while the 'cmd' argument can be tokenize()d and each element retrieved via argv(argno). Unrecognised cmds MUST be passed on to the clientcommand builtin."}, + {"SV_ParseClusterEvent", "noref void(string dest, string from, string cmd, string info)", QW|NQ, "Part of cluster mode. Handles cross-node events that were sent via clusterevent, on behalf of the named client."}, {"SV_ParseConnectionlessPacket", "noref float(string sender, string body)", QW|NQ, "Provides QC with a way to communicate between servers, or with client server browsers. Sender is the sender's ip. Body is the body of the message. You'll need to add your own password/etc support as required. Self is not valid."}, {"SV_PausedTic", "noref void(float pauseduration)", QW|NQ, "For each frame that the server is paused, this function will be called to give the gamecode a chance to unpause the server again. the pauseduration argument says how long the server has been paused for (the time global is frozen and will not increment while paused). Self is not valid."}, {"SV_ShouldPause", "noref float(float newstatus)", QW|NQ, "Called to give the qc a change to block pause/unpause requests. Return false for the pause request to be ignored. newstatus is 1 if the user is trying to pause the game. For the duration of the call, self will be set to the player who tried to pause, or to world if it was triggered by a server-side event."}, - {"ClassChangeWeapon", "noref void()", H2, "Hexen2 support. Called when cl_playerclass changes. Self is set to the player who is changing class."}, {"SV_RunClientCommand", "noref void()", QW|NQ, "Called each time a player movement packet was received from a client. Self is set to the player entity which should be updated, while the input_* globals specify the various properties stored within the input packet. The contents of this function should be somewaht identical to the equivelent function in CSQC, or prediction misses will occur. If you're feeling lazy, you can simply call 'runstandardplayerphysics' after modifying the inputs."}, {"SV_AddDebugPolygons", "noref void()", QW|NQ, "Called each video frame. This is the only place where ssqc is allowed to call the R_BeginPolygon/R_PolygonVertex/R_EndPolygon builtins. This is exclusively for debugging, and will break in anything but single player as it will not be called if the engine is not running both a client and a server."}, {"SV_PlayerPhysics", "noref void()", QW|NQ, "Legacy method to tweak player input that does not reliably work with prediction (prediction WILL break). Mods that care about prediction should use SV_RunClientCommand instead. If pr_no_playerphysics is set to 1, this function will never be called, which will either fix prediction or completely break player movement depending on whether the feature was even useful."}, {"EndFrame", "noref void()", QW|NQ, "Called after non-player entities have been run at the end of the physics frame. Player physics is performed out of order and can/will still occur between EndFrame and BeginFrame."}, {"SV_CheckRejectConnection","noref string(string addr, string uinfo, string features) ", QW|NQ, "Called to give the mod a chance to ignore connection requests based upon client protocol support or other properties. Use infoget to read the uinfo and features arguments."}, + {"ClassChangeWeapon", "noref void()", H2, "Hexen2 support. Called when cl_playerclass changes. Self is set to the player who is changing class."}, /* //mvdsv compat {"UserInfo_Changed", "//noref void()", QW}, {"localinfoChanged", "//noref void()", QW}, @@ -10928,9 +10964,9 @@ void PR_DumpPlatform_f(void) if (!strcmp(knowndefs[i].type, "const float")) { if (defines) - VFS_PRINTF(f, "#define %s %i%s\n", knowndefs[i].name, knowndefs[i].value, comment); + VFS_PRINTF(f, "#define %s %g%s\n", knowndefs[i].name, knowndefs[i].value, comment); else - VFS_PRINTF(f, "%s %s = %i;%s\n", knowndefs[i].type, knowndefs[i].name, knowndefs[i].value, comment); + VFS_PRINTF(f, "%s %s = %g;%s\n", knowndefs[i].type, knowndefs[i].name, knowndefs[i].value, comment); } else if (!strcmp(knowndefs[i].type, "const string")) { @@ -10945,7 +10981,7 @@ void PR_DumpPlatform_f(void) } else if (knowndefs[i].value) { - VFS_PRINTF(f, "%s %s = %i;%s\n", knowndefs[i].type, knowndefs[i].name, knowndefs[i].value, comment); + VFS_PRINTF(f, "%s %s = %g;%s\n", knowndefs[i].type, knowndefs[i].name, knowndefs[i].value, comment); } else VFS_PRINTF(f, "%s %s;%s\n", knowndefs[i].type, knowndefs[i].name, comment); diff --git a/engine/server/progs.h b/engine/server/progs.h index 78f675671..f1ef089c7 100644 --- a/engine/server/progs.h +++ b/engine/server/progs.h @@ -38,6 +38,7 @@ void PR_Init(void); void QDECL ED_Spawned (struct edict_s *ent, int loading); qboolean SV_RunFullQCMovement(struct client_s *client, usercmd_t *ucmd); qboolean PR_KrimzonParseCommand(char *s); +qboolean PR_ParseClusterEvent(char *dest, char *source, char *cmd, char *info); qboolean PR_UserCmd(char *cmd); qboolean PR_ConsoleCmd(const char *cmd); diff --git a/engine/server/savegame.c b/engine/server/savegame.c index bbe0fdf2c..b008f6c14 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -163,6 +163,7 @@ void SV_Loadgame_Legacy(char *filename, vfsfile_t *f, int version) cl->state = cs_loadzombie; cl->connection_started = realtime+20; cl->istobeloaded = true; + cl->userid = 0; //probably should be 32, rather than NUM_SPAWN_PARMS(64) for (i=0 ; istate = cs_loadzombie; cl->connection_started = realtime+20; cl->istobeloaded = true; + cl->userid = 0; loadzombies++; memset(&cl->netchan, 0, sizeof(cl->netchan)); diff --git a/engine/server/server.h b/engine/server/server.h index 5e136ce30..076ad3b4d 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -378,9 +378,6 @@ typedef struct client_s int challenge; int userid; // identifying number char userinfo[EXTENDED_INFO_STRING]; // infostring -#ifdef SUBSERVERS - unsigned int previousserver; -#endif usercmd_t lastcmd; // for filling in big drops and partial predictions double localtime; // of last message @@ -1024,13 +1021,22 @@ void SSV_PollSlaves(void); void SSV_InstructMaster(sizebuf_t *cmd); void SSV_PrintToMaster(char *s); void SSV_ReadFromControlServer(void); -void SSV_SavePlayerStats(client_t *cl, unsigned int previousserver); +void SSV_SavePlayerStats(client_t *cl, int reason); //initial, periodic (in case of node crashes), part void Sys_InstructSlave(pubsubserver_t *s, sizebuf_t *cmd); int Sys_SubServerRead(pubsubserver_t *s); //1: yes. 0: no. -1: error pubsubserver_t *Sys_ForkServer(void); #define SSV_IsSubServer() isClusterSlave + + +void MSV_SubServerCommand_f(void); +void MSV_SubServerCommand_f(void); +void MSV_MapCluster_f(void); +void SSV_Send(const char *dest, const char *src, const char *cmd, const char *msg); +qboolean MSV_ClusterLogin(char *guid, char *userinfo, size_t userinfosize); +void MSV_PollSlaves(void); +void MSV_Status(void); #else #define SSV_UpdateAddresses() false #define MSV_ClusterLogin(guid,info,infosize) false @@ -1040,7 +1046,6 @@ pubsubserver_t *Sys_ForkServer(void); // // sv_init.c // -void SV_SpawnClusterMode(void); void SV_SpawnServer (char *server, char *startspot, qboolean noents, qboolean usecinematic); void SV_UnspawnServer (void); void SV_FlushSignon (void); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index a7dc24f7f..feab03116 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -1708,8 +1708,15 @@ static void SV_Status_f (void) if (NET_GetRates(svs.sockets, &pi, &po, &bi, &bo)) Con_Printf("packets,bytes/sec: in: %g %g out: %g %g\n", pi, bi, po, bo); //not relevent as a limit. Con_Printf("server uptime : %s\n", ShowTime(realtime)); + Con_Printf("public : %s\n", sv_public.value?"yes":"no"); + Con_Printf("client types :%s%s%s%s\n", sv_listen_qw.ival?" QW":"", sv_listen_nq.ival?" NQ":"", sv_listen_dp.ival?" DP":"", sv_listen_q3.ival?" Q3":""); +#ifdef SUBSERVERS if (sv.state == ss_clustermode) + { + MSV_Status(); return; + } +#endif Con_Printf("map uptime : %s\n", ShowTime(sv.world.physicstime)); //show the current map+name (but hide name if its too long or would be ugly) if (columns >= 80 && *sv.mapname && strlen(sv.mapname) < 45 && !strchr(sv.mapname, '\n')) @@ -1735,8 +1742,6 @@ static void SV_Status_f (void) Con_Printf("csqc debug : true\n"); if (sv.mvdrecording) Con_Printf("recording : %s\n", SV_Demo_CurrentOutput()); - Con_Printf("public : %s\n", sv_public.value?"yes":"no"); - Con_Printf("client types :%s%s%s%s\n", sv_listen_qw.ival?" QW":"", sv_listen_nq.ival?" NQ":"", sv_listen_dp.ival?" DP":"", sv_listen_q3.ival?" Q3":""); // min fps lat drp if (columns < 80) diff --git a/engine/server/sv_cluster.c b/engine/server/sv_cluster.c new file mode 100644 index 000000000..b49bd0556 --- /dev/null +++ b/engine/server/sv_cluster.c @@ -0,0 +1,1088 @@ +#include "quakedef.h" +#include "netinc.h" + +#ifdef SUBSERVERS + +#ifdef SQL +#include "sv_sql.h" +#endif + +extern cvar_t sv_serverip; + +void VARGS SV_RejectMessage(int protocol, char *format, ...); + + +void MSV_UpdatePlayerStats(unsigned int playerid, unsigned int serverid, int numstats, float *stats); + +static char *knownmaps[] = +{ + "", + "start" +}; + +typedef struct { + //fixme: hash tables + unsigned int playerid; + char name[64]; + char guid[64]; + char address[64]; + + link_t allplayers; +// link_t sameserver; + + pubsubserver_t *server; //should never be null +} clusterplayer_t; + +static pubsubserver_t *subservers; +static link_t clusterplayers; +qboolean isClusterSlave; +unsigned int nextserverid; + +static clusterplayer_t *MSV_FindPlayerId(unsigned int playerid) +{ + link_t *l; + clusterplayer_t *pl; + + FOR_EACH_LINK(l, clusterplayers) + { + pl = STRUCT_FROM_LINK(l, clusterplayer_t, allplayers); + if (pl->playerid == playerid) + return pl; + } + return NULL; +} +static clusterplayer_t *MSV_FindPlayerName(char *playername) +{ + link_t *l; + clusterplayer_t *pl; + + FOR_EACH_LINK(l, clusterplayers) + { + pl = STRUCT_FROM_LINK(l, clusterplayer_t, allplayers); + if (!strcmp(pl->name, playername)) + return pl; + } + return NULL; +} +static void MSV_ServerCrashed(pubsubserver_t *server) +{ + link_t *l, *next; + clusterplayer_t *pl; + + //forget any players that are meant to be on this server. + for (l = clusterplayers.next ; l != &clusterplayers ; l = next) + { + next = l->next; + pl = STRUCT_FROM_LINK(l, clusterplayer_t, allplayers); + if (pl->server == server) + { + Con_Printf("%s(%s) crashed out\n", pl->name, server->name); + RemoveLink(&pl->allplayers); + Z_Free(pl); + } + } + + Z_Free(server); +} + +pubsubserver_t *MSV_StartSubServer(unsigned int id, const char *mapname) +{ + sizebuf_t send; + char send_buf[64]; + pubsubserver_t *s = Sys_ForkServer(); + if (s) + { + if (!id) + { + if (nextserverid < sizeof(knownmaps)/sizeof(knownmaps[0])) + nextserverid = sizeof(knownmaps)/sizeof(knownmaps[0]); + id = nextserverid++; + } + s->id = id; + s->next = subservers; + subservers = s; + + Q_strncpyz(s->name, mapname, sizeof(s->name)); + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + MSG_WriteByte(&send, ccmd_acceptserver); + MSG_WriteLong(&send, s->id); + MSG_WriteString(&send, s->name); + Sys_InstructSlave(s, &send); + } + return s; +} + +pubsubserver_t *MSV_FindSubServer(unsigned int id) +{ + pubsubserver_t *s; + for (s = subservers; s; s = s->next) + { + if (id == s->id) + return s; + } + + if (!s && id >= 1 && id < sizeof(knownmaps)/sizeof(knownmaps[0])) + s = MSV_StartSubServer(id, knownmaps[id]); + + return s; +} +pubsubserver_t *MSV_FindSubServerName(const char *mapname) +{ + pubsubserver_t *s; + unsigned int to; + for (to = 1; to < sizeof(knownmaps)/sizeof(knownmaps[0]); to++) + { + if (!strcmp(knownmaps[to], mapname)) + return MSV_FindSubServer(to); + } + + for (s = subservers; s; s = s->next) + { + if (!strcmp(s->name, mapname)) + return s; + } + + return MSV_StartSubServer(0, mapname); +} +qboolean MSV_AddressForMap(netadr_t *ret, int natype, int serverid) +{ + pubsubserver_t *s = MSV_FindSubServer(serverid); + + if (s) + { + if (natype == s->addrv6.type) + *ret = s->addrv6; + else + *ret = s->addrv4; + return true; + } + return false; +} + +void MSV_InstructSlave(unsigned int id, sizebuf_t *cmd) +{ + pubsubserver_t *s; + if (!id) + { + for (s = subservers; s; s = s->next) + Sys_InstructSlave(s, cmd); + } + else + { + s = MSV_FindSubServer(id); + if (s) + Sys_InstructSlave(s, cmd); + } +} + +void SV_SetupNetworkBuffers(qboolean bigcoords); + +void MSV_MapCluster_f(void) +{ + char *sqlparams[] = + { + "", + "", + "", + "login", + }; + + //this command will likely be used in configs. don't ever allow subservers to act as entire new clusters + if (SSV_IsSubServer()) + return; + +#ifndef SERVERONLY + CL_Disconnect(); +#endif + + if (sv.state) + SV_UnspawnServer(); + NET_InitServer(); + + //child processes return 0 and fall through + memset(&sv, 0, sizeof(sv)); + if (Cmd_Argc() > 1) + { + char *dbname = Cmd_Argv(1); + char *sqlparams[] = + { + "", //host + "", //username + "", //password + dbname, //db + }; + Con_Printf("Opening database \"%s\"\n", dbname); + sv.logindatabase = SQL_NewServer("sqlite", sqlparams); + if (sv.logindatabase == -1) + { + SV_UnspawnServer(); + Con_Printf("Unable to open account database\n"); + return; + } + } + else + { + sv.logindatabase = -1; + Con_Printf("Operating in databaseless mode\n"); + } + sv.state = ss_clustermode; + ClearLink(&clusterplayers); + + //and for legacy clients, we need some server stuff inited. + SV_SetupNetworkBuffers(false); + SV_UpdateMaxPlayers(32); +} + +void SSV_PrintToMaster(char *s) +{ + sizebuf_t send; + char send_buf[8192]; + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + MSG_WriteByte(&send, ccmd_print); + MSG_WriteString(&send, s); + SSV_InstructMaster(&send); +} + +void MSV_Status(void) +{ + link_t *l; + char bufmem[1024]; + pubsubserver_t *s; + clusterplayer_t *pl; + for (s = subservers; s; s = s->next) + { + Con_Printf("%i: %s", s->id, s->name); + if (s->addrv4.type != NA_INVALID) + Con_Printf(" %s", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv4)); + if (s->addrv6.type != NA_INVALID) + Con_Printf(" %s", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv6)); + Con_Printf("\n"); + } + + FOR_EACH_LINK(l, clusterplayers) + { + pl = STRUCT_FROM_LINK(l, clusterplayer_t, allplayers); + Con_Printf("%i(%s): (%s) %s (%s)\n", pl->playerid, pl->server->name, pl->guid, pl->name, pl->address); + } +} +void MSV_SubServerCommand_f(void) +{ + sizebuf_t buf; + char bufmem[1024]; + pubsubserver_t *s; + int id; + char *c; + if (Cmd_Argc() == 1) + { + Con_Printf("Active servers on this cluster:\n"); + for (s = subservers; s; s = s->next) + { + Con_Printf("%i: %s", s->id, s->name); + if (s->addrv4.type != NA_INVALID) + Con_Printf(" %s", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv4)); + if (s->addrv6.type != NA_INVALID) + Con_Printf(" %s", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv6)); + Con_Printf("\n"); + } + return; + } + if (!strcmp(Cmd_Argv(0), "ssv_all")) + id = 0; + else + { + id = atoi(Cmd_Argv(1)); + Cmd_ShiftArgs(1, false); + } + + buf.data = bufmem; + buf.maxsize = sizeof(bufmem); + buf.cursize = 2; + buf.packing = SZ_RAWBYTES; + c = Cmd_Args(); + MSG_WriteByte(&buf, ccmd_stuffcmd); + MSG_WriteString(&buf, c); + buf.data[0] = buf.cursize & 0xff; + buf.data[1] = (buf.cursize>>8) & 0xff; + MSV_InstructSlave(id, &buf); +} + +void MSV_ReadFromSubServer(pubsubserver_t *s) +{ + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + netadr_t adr; + char *str; + int c; + + c = MSG_ReadByte(); + switch(c) + { + default: + case ccmd_bad: + Sys_Error("Corrupt message (%i) from SubServer %i:%s", c, s->id, s->name); + break; + case ccmd_print: + Con_Printf("^6%i(%s)^7: %s", s->id, s->name, MSG_ReadString()); + break; + case ccmd_saveplayer: + { + clusterplayer_t *pl; + float stats[NUM_SPAWN_PARMS]; + int i; + unsigned char reason = MSG_ReadByte(); + unsigned int plid = MSG_ReadLong(); + int numstats = MSG_ReadByte(); + numstats = min(numstats, NUM_SPAWN_PARMS); + for (i = 0; i < numstats; i++) + stats[i] = MSG_ReadFloat(); + + pl = MSV_FindPlayerId(plid); + if (!pl) + { + Con_Printf("player %u(%s) does not exist!\n", plid, s->name); + return; + } + //player already got taken by a different server, don't save stale data. + if (reason && pl->server != s) + return; + + MSV_UpdatePlayerStats(plid, s->id, numstats, stats); + + switch (reason) + { + case 0: //server reports that it accepted the player + if (pl->server && pl->server != s) + { //let the previous server know + sizebuf_t send; + qbyte send_buf[64]; + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + MSG_WriteByte(&send, ccmd_transferedplayer); + MSG_WriteLong(&send, s->id); + MSG_WriteLong(&send, plid); + Sys_InstructSlave(pl->server, &send); + } + pl->server = s; + break; + case 2: //drop + case 3: //transfer abort + if (pl->server == s) + { + Con_Printf("%s(%s) dropped\n", pl->name, s->name); + RemoveLink(&pl->allplayers); + Z_Free(pl); + } + break; + } + } + break; + case ccmd_transferplayer: + { //server is offering a player to another server + char guid[64]; + char mapname[64]; + char plnamebuf[64]; + int plid = MSG_ReadLong(); + char *plname = MSG_ReadStringBuffer(plnamebuf, sizeof(plnamebuf)); + char *newmap = MSG_ReadStringBuffer(mapname, sizeof(mapname)); + char *claddr = MSG_ReadString(); + char *clguid = MSG_ReadStringBuffer(guid, sizeof(guid)); + pubsubserver_t *toptr; + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + if (NULL!=(toptr=MSV_FindSubServerName(newmap)) && s != toptr) + { +// Con_Printf("Transfer to %i:%s\n", toptr->id, toptr->name); + + MSG_WriteByte(&send, ccmd_takeplayer); + MSG_WriteLong(&send, plid); + MSG_WriteString(&send, plname); + MSG_WriteLong(&send, s->id); + MSG_WriteString(&send, claddr); + MSG_WriteString(&send, clguid); + + c = MSG_ReadByte(); + MSG_WriteByte(&send, c); +// Con_Printf("Transfer %i stats\n", c); + while(c--) + MSG_WriteFloat(&send, MSG_ReadFloat()); + + Sys_InstructSlave(toptr, &send); + } + else + { + //suck up the stats + c = MSG_ReadByte(); + while(c--) + MSG_ReadFloat(); + +// Con_Printf("Transfer abort\n"); + + MSG_WriteByte(&send, ccmd_tookplayer); + MSG_WriteLong(&send, s->id); + MSG_WriteLong(&send, plid); + MSG_WriteString(&send, ""); + + Sys_InstructSlave(s, &send); + } + } + break; + case ccmd_tookplayer: + { //server has space, and wants the client. + int to = MSG_ReadLong(); + int plid = MSG_ReadLong(); + char *claddr = MSG_ReadString(); + char *rmsg; + netadr_t cladr; + netadr_t svadr; + char adrbuf[256]; + +// Con_Printf("Took player\n"); + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + NET_StringToAdr(claddr, 0, &cladr); + MSV_AddressForMap(&svadr, cladr.type, s->id); + if (!to) + { + if (svadr.type != NA_INVALID) + { + rmsg = va("fredir\n%s", NET_AdrToString(adrbuf, sizeof(adrbuf), &svadr)); + Netchan_OutOfBand (NS_SERVER, &cladr, strlen(rmsg), (qbyte *)rmsg); + } + } + else + { + MSG_WriteByte(&send, ccmd_tookplayer); + MSG_WriteLong(&send, s->id); + MSG_WriteLong(&send, plid); + MSG_WriteString(&send, NET_AdrToString(adrbuf, sizeof(adrbuf), &svadr)); + + MSV_InstructSlave(to, &send); + } + } + break; + case ccmd_serveraddress: + s->addrv4.type = NA_INVALID; + s->addrv6.type = NA_INVALID; + str = MSG_ReadString(); + Q_strncpyz(s->name, str, sizeof(s->name)); + for (;;) + { + str = MSG_ReadString(); + if (!*str) + break; + if (NET_StringToAdr(str, 0, &adr)) + { + if (adr.type == NA_IP && s->addrv4.type == NA_INVALID) + s->addrv4 = adr; + if (adr.type == NA_IPV6 && s->addrv6.type == NA_INVALID) + s->addrv6 = adr; + } + } + Con_Printf("%i:%s: restarted\n", s->id, s->name); + break; + case ccmd_stringcmd: + { + char dest[1024]; + char from[1024]; + char cmd[1024]; + char info[1024]; + MSG_ReadStringBuffer(dest, sizeof(dest)); + MSG_ReadStringBuffer(from, sizeof(from)); + MSG_ReadStringBuffer(cmd, sizeof(cmd)); + MSG_ReadStringBuffer(info, sizeof(info)); + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + MSG_WriteByte(&send, ccmd_stringcmd); + MSG_WriteString(&send, dest); + MSG_WriteString(&send, from); + MSG_WriteString(&send, cmd); + MSG_WriteString(&send, info); + + if (!*dest) //broadcast if no dest + { + for (s = subservers; s; s = s->next) + Sys_InstructSlave(s, &send); + } + else + { + //send it to the server that the player is currently on. + clusterplayer_t *pl = MSV_FindPlayerName(dest); + if (pl) + Sys_InstructSlave(pl->server, &send); + else if (!pl && strncmp(cmd, "error:", 6)) + { + //player not found. send it back to the sender, but add an error prefix. + send.cursize = 2; + MSG_WriteByte(&send, ccmd_stringcmd); + MSG_WriteString(&send, from); + MSG_WriteString(&send, dest); + SZ_Write(&send, "error:", 6); + MSG_WriteString(&send, cmd); + MSG_WriteString(&send, info); + Sys_InstructSlave(s, &send); + } + } + } + break; + } + if (msg_readcount != net_message.cursize || msg_badread) + Sys_Error("Master: Readcount isn't right (%i)\n", net_message.data[0]); +} + +void MSV_PollSlaves(void) +{ + pubsubserver_t **link, *s; + for (link = &subservers; (s=*link); ) + { + switch(Sys_SubServerRead(s)) + { + case -1: + //error - server is dead and needs to be freed. + *link = s->next; + MSV_ServerCrashed(s); + break; + case 0: + //no messages + link = &s->next; + break; + case 1: + //got a message. read it and see if there's more. + MSV_ReadFromSubServer(s); + break; + } + } +} + +void SSV_ReadFromControlServer(void) +{ + int c; + char *s; + + c = MSG_ReadByte(); + switch(c) + { + case ccmd_bad: + default: + SV_Error("Invalid message from cluster (%i)\n", c); + break; + case ccmd_stuffcmd: + s = MSG_ReadString(); + SV_BeginRedirect(RD_MASTER, 0); + Cmd_ExecuteString(s, RESTRICT_LOCAL); + SV_EndRedirect(); + break; + + case ccmd_acceptserver: + svs.clusterserverid = MSG_ReadLong(); + s = MSG_ReadString(); + if (*s && !strchr(s, ';') && !strchr(s, '\n') && !strchr(s, '\"')) //sanity check the argument + Cmd_ExecuteString(va("map \"%s\"", s), RESTRICT_LOCAL); + if (svprogfuncs && pr_global_ptrs->serverid) + *pr_global_ptrs->serverid = svs.clusterserverid; + break; + + case ccmd_tookplayer: + { + client_t *cl = NULL; + int to = MSG_ReadLong(); + int plid = MSG_ReadLong(); + char *addr = MSG_ReadString(); + int i; + + Con_Printf("%s: got tookplayer\n", sv.name); + + for (i = 0; i < svs.allocated_client_slots; i++) + { + if (svs.clients[i].state && svs.clients[i].userid == plid) + { + cl = &svs.clients[i]; + break; + } + } + if (cl) + { + if (!*addr) + { + Con_Printf("%s: tookplayer: failed\n", sv.name); + Info_SetValueForStarKey(cl->userinfo, "*transfer", "", sizeof(cl->userinfo)); + } + else + { + Con_Printf("%s: tookplayer: do transfer\n", sv.name); +// SV_StuffcmdToClient(cl, va("connect \"%s\"\n", addr)); + SV_StuffcmdToClient(cl, va("cl_transfer \"%s\"\n", addr)); + cl->redirect = 2; + } + } + else + Con_Printf("%s: tookplayer: invalid player.\n", sv.name); + } + break; + + case ccmd_transferedplayer: + { + client_t *cl; + char *to; + int toserver = MSG_ReadLong(); + int playerid = MSG_ReadLong(); + int i; + + for (i = 0; i < svs.allocated_client_slots; i++) + { + if (svs.clients[i].userid == playerid && svs.clients[i].state >= cs_loadzombie) + { + cl = &svs.clients[i]; + cl->drop = true; + to = Info_ValueForKey(cl->userinfo, "*transfer"); + Con_Printf("%s transfered to %s\n", cl->name, to); + break; + } + } + } + break; + + case ccmd_takeplayer: + { + client_t *cl = NULL; + int i, j; + float stat; + char guid[64], name[64]; + int plid = MSG_ReadLong(); + char *plname = MSG_ReadStringBuffer(name, sizeof(name)); + int fromsv = MSG_ReadLong(); + char *claddr = MSG_ReadString(); + char *clguid = MSG_ReadStringBuffer(guid, sizeof(guid)); + + if (sv.state >= ss_active) + { + for (i = 0; i < svs.allocated_client_slots; i++) + { + if (!svs.clients[i].state || (svs.clients[i].userid == plid && svs.clients[i].state >= cs_loadzombie)) + { + cl = &svs.clients[i]; + break; + } + } + } + +// Con_Printf("%s: takeplayer\n", sv.name); + if (cl) + { + cl->userid = plid; + if (cl->state == cs_loadzombie && cl->istobeloaded) + cl->connection_started = realtime+20; //renew the slot + else if (!cl->state) + { //allocate a new pending player. + cl->state = cs_loadzombie; + cl->connection_started = realtime+20; + Q_strncpyz(cl->guid, clguid, sizeof(cl->guid)); + Q_strncpyz(cl->namebuf, plname, sizeof(cl->namebuf)); + cl->name = cl->namebuf; + sv.spawned_client_slots++; + memset(&cl->netchan, 0, sizeof(cl->netchan)); + SV_GetNewSpawnParms(cl); + } + //else: already on the server somehow. laggy/dupe request? must be. + } + else + { + Con_Printf("%s: server full!\n", sv.name); + } + + j = MSG_ReadByte(); +// Con_Printf("%s: %i stats\n", sv.name, j); + for (i = 0; i < j; i++) + { + stat = MSG_ReadFloat(); + if (cl && cl->state == cs_loadzombie && i < NUM_SPAWN_PARMS) + cl->spawn_parms[i] = stat; + } + + { + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + if (cl) + { + MSG_WriteByte(&send, ccmd_tookplayer); + MSG_WriteLong(&send, fromsv); + MSG_WriteLong(&send, plid); + MSG_WriteString(&send, claddr); + SSV_InstructMaster(&send); + } + } + } + break; + case ccmd_stringcmd: + { + char dest[1024]; + char from[1024]; + char cmd[1024]; + char info[1024]; + int i; + client_t *cl; + MSG_ReadStringBuffer(dest, sizeof(dest)); + MSG_ReadStringBuffer(from, sizeof(from)); + MSG_ReadStringBuffer(cmd, sizeof(cmd)); + MSG_ReadStringBuffer(info, sizeof(info)); + + if (!PR_ParseClusterEvent(dest, from, cmd, info)) + { + //meh, lets make some lame fallback thing + for (i = 0; i < sv.allocated_client_slots; i++) + { + cl = &svs.clients[i]; + if (!*dest || !strcmp(dest, cl->name)) + { + SV_PrintToClient(cl, PRINT_HIGH, va("%s from [%s]: %s\n", cmd, from, info)); + if (*dest) + break; + } + } + } + } + break; + } + + if (msg_readcount != net_message.cursize || msg_badread) + Sys_Error("Subserver: Readcount isn't right (%i)\n", net_message.data[0]); +} + +void SSV_UpdateAddresses(void) +{ + char buf[256]; + netadr_t addr[64]; + struct ftenet_generic_connection_s *con[sizeof(addr)/sizeof(addr[0])]; + int flags[sizeof(addr)/sizeof(addr[0])]; + int count; + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + int i; + + if (!SSV_IsSubServer()) + return; + + count = NET_EnumerateAddresses(svs.sockets, con, flags, addr, sizeof(addr)/sizeof(addr[0])); + + if (*sv_serverip.string) + { + for (i = 0; i < count; i++) + { + if (addr[i].type == NA_IP) + { + NET_StringToAdr(sv_serverip.string, BigShort(addr[i].port), &addr[0]); + count = 1; + break; + } + } + } + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + MSG_WriteByte(&send, ccmd_serveraddress); + + MSG_WriteString(&send, sv.name); + for (i = 0; i < count; i++) + MSG_WriteString(&send, NET_AdrToString(buf, sizeof(buf), &addr[i])); + MSG_WriteByte(&send, 0); + SSV_InstructMaster(&send); +} + +void SSV_SavePlayerStats(client_t *cl, int reason) +{ + //called when the *transfer userinfo gets set to the new map + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + int i; + if (!SSV_IsSubServer()) + return; + + if ((reason == 1 || reason == 2) && cl->edict) + SV_SaveSpawnparmsClient(cl, NULL); + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + MSG_WriteByte(&send, ccmd_saveplayer); + MSG_WriteByte(&send, reason); + MSG_WriteLong(&send, cl->userid); + MSG_WriteByte(&send, NUM_SPAWN_PARMS); + for (i = 0; i < NUM_SPAWN_PARMS; i++) + { + MSG_WriteFloat(&send, cl->spawn_parms[i]); + } + + SSV_InstructMaster(&send); +} +void SSV_Send(const char *dest, const char *src, const char *cmd, const char *msg) +{ + //called when the *transfer userinfo gets set to the new map + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + if (!SSV_IsSubServer()) + return; + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + MSG_WriteByte(&send, ccmd_stringcmd); + MSG_WriteString(&send, dest?dest:""); + MSG_WriteString(&send, src?src:""); + MSG_WriteString(&send, cmd?cmd:""); + MSG_WriteString(&send, msg?msg:""); + + SSV_InstructMaster(&send); +} +void SSV_InitiatePlayerTransfer(client_t *cl, const char *newserver) +{ + //called when the *transfer userinfo gets set to the new map + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + int i; + char tmpbuf[256]; + float parms[NUM_SPAWN_PARMS]; + + SV_SaveSpawnparmsClient(cl, parms); + + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + MSG_WriteByte(&send, ccmd_transferplayer); + MSG_WriteLong(&send, cl->userid); + MSG_WriteString(&send, cl->name); + MSG_WriteString(&send, newserver); + MSG_WriteString(&send, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &cl->netchan.remote_address)); + MSG_WriteString(&send, cl->guid); + + //stats + MSG_WriteByte(&send, NUM_SPAWN_PARMS); + for (i = 0; i < NUM_SPAWN_PARMS; i++) + { + MSG_WriteFloat(&send, parms[i]); + } + + SSV_InstructMaster(&send); +} + +#ifdef SQL +#include "sv_sql.h" +int pendinglookups = 0; +struct logininfo_s +{ + netadr_t clientaddr; + char guid[64]; + char name[64]; +}; +#endif +qboolean SV_IgnoreSQLResult(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof) +{ + return false; +} +void MSV_UpdatePlayerStats(unsigned int playerid, unsigned int serverid, int numstats, float *stats) +{ + queryrequest_t *req; + sqlserver_t *srv; + static char hex[16] = "0123456789abcdef"; + char sql[2048], *sqle; + union{float *f;qbyte *b;} blob; + if (sv.logindatabase != -1) + { + Q_snprintfz(sql, sizeof(sql), "UPDATE accounts SET stats=x'"); + sqle = sql+strlen(sql); + for (blob.f = stats, numstats*=4; numstats--; blob.b++) + { + *sqle++ = hex[*blob.b>>4]; + *sqle++ = hex[*blob.b&15]; + } + Q_snprintfz(sqle, sizeof(sql)-(sqle-sql), "', serverid=%u WHERE playerid = %u;", serverid, playerid); + + srv = SQL_GetServer(sv.logindatabase, false); + if (srv) + SQL_NewQuery(srv, SV_IgnoreSQLResult, sql, &req); + } +} + +qboolean MSV_ClusterLoginReply(netadr_t *legacyclientredirect, unsigned int serverid, unsigned int playerid, char *playername, char *clientguid, netadr_t *clientaddr, void *statsblob, size_t statsblobsize) +{ + char tmpbuf[256]; + netadr_t serveraddr; + + if (!serverid) + serverid = 1; + + if (!MSV_AddressForMap(&serveraddr, clientaddr->type, serverid) && !MSV_AddressForMap(&serveraddr, clientaddr->type, serverid=1)) + SV_RejectMessage(SCP_QUAKEWORLD, "Unable to find lobby.\n"); + else + { + pubsubserver_t *s; + sizebuf_t send; + qbyte send_buf[MAX_QWMSGLEN]; + clusterplayer_t *pl; + memset(&send, 0, sizeof(send)); + send.data = send_buf; + send.maxsize = sizeof(send_buf); + send.cursize = 2; + + pl = Z_Malloc(sizeof(*pl)); + Q_strncpyz(pl->name, playername, sizeof(pl->name)); + Q_strncpyz(pl->guid, clientguid, sizeof(pl->guid)); + NET_AdrToString(pl->address, sizeof(pl->address), clientaddr); + pl->playerid = playerid; + InsertLinkBefore(&pl->allplayers, &clusterplayers); + pl->server = s = MSV_FindSubServer(serverid); + + MSG_WriteByte(&send, ccmd_takeplayer); + MSG_WriteLong(&send, playerid); + MSG_WriteString(&send, pl->name); + MSG_WriteLong(&send, 0); //from server + MSG_WriteString(&send, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &net_from)); + MSG_WriteString(&send, clientguid); + + MSG_WriteByte(&send, statsblobsize/4); + SZ_Write(&send, statsblob, statsblobsize&~3); + Sys_InstructSlave(s, &send); + + if (serveraddr.type == NA_INVALID) + { + if (net_from.type != NA_LOOPBACK) + SV_RejectMessage(SCP_QUAKEWORLD, "Starting instance.\n"); + } + else if (legacyclientredirect) + { + *legacyclientredirect = serveraddr; + return true; + } + else + { + char *s = va("fredir\n%s", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &serveraddr)); + Netchan_OutOfBand (NS_SERVER, clientaddr, strlen(s), (qbyte *)s); + return true; + } + } + return false; +} + +qboolean MSV_ClusterLoginSQLResult(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof) +{ + sqlserver_t *sql = SQL_GetServer(req->srvid, true); + queryresult_t *res = SQL_GetQueryResult(sql, req->num, 0); + struct logininfo_s *info = req->user.thread; + char *s; + int playerid, serverid; + char *statsblob; + size_t blobsize; + + res = SQL_GetQueryResult(sql, req->num, 0); + if (!res) + { + playerid = 0; + statsblob = NULL; + blobsize = 0; + serverid = 0; + } + else + { + s = SQL_ReadField(sql, res, 0, 0, true, NULL); + playerid = atoi(s); + + statsblob = SQL_ReadField(sql, res, 0, 2, true, &blobsize); + + s = SQL_ReadField(sql, res, 0, 1, true, NULL); + serverid = s?atoi(s):0; + } + + net_from = info->clientaddr; //okay, that's a bit stupid, rewrite rejectmessage to accept an arg? + if (!playerid) + SV_RejectMessage(SCP_QUAKEWORLD, "Bad username or password.\n"); + else + MSV_ClusterLoginReply(NULL, serverid, playerid, info->name, info->guid, &info->clientaddr, statsblob, blobsize); + Z_Free(info); + pendinglookups--; + return false; +} + +//returns true to block entry to this server. +extern int nextuserid; +qboolean MSV_ClusterLogin(char *guid, char *userinfo, size_t userinfosize) +{ + char escname[64], escpasswd[64]; + sqlserver_t *sql; + queryrequest_t *req; + struct logininfo_s *info; + + if (sv.state != ss_clustermode) + return false; + + /*if (!*guid) + { + SV_RejectMessage(SCP_QUAKEWORLD, "No guid info, please set cl_sendguid to 1.\n"); + return false; + }*/ + + if (sv.logindatabase != -1) + { + if (pendinglookups > 10) + return true; + sql = SQL_GetServer(sv.logindatabase, false); + if (!sql) + return true; + SQL_Escape(sql, Info_ValueForKey(userinfo, "name"), escname, sizeof(escname)); + SQL_Escape(sql, Info_ValueForKey(userinfo, "password"), escpasswd, sizeof(escpasswd)); + if (SQL_NewQuery(sql, MSV_ClusterLoginSQLResult, va("SELECT playerid,serverid,stats FROM accounts WHERE name='%s' AND password='%s';", escname, escpasswd), &req) != -1) + { + pendinglookups++; + req->user.thread = info = Z_Malloc(sizeof(*info)); + Q_strncpyz(info->guid, guid, sizeof(info->guid)); + info->clientaddr = net_from; + } + } +/* else if (0) + { + char tmpbuf[256]; + netadr_t redir; + if (MSV_ClusterLoginReply(&redir, 0, nextuserid++, guid, &net_from, NULL, 0)) + { + Info_SetValueForStarKey(userinfo, "*redirect", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &redir), userinfosize); + return false; + } + return true; + }*/ + else + MSV_ClusterLoginReply(NULL, 0, ++nextuserid, Info_ValueForKey(userinfo, "name"), guid, &net_from, NULL, 0); + return true; +} +#endif diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 201518a2d..6747153a7 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -664,7 +664,7 @@ void SV_UpdateMaxPlayers(int newmax) sv.allocated_client_slots = svs.allocated_client_slots; } -static void SV_SetupNetworkBuffers(qboolean bigcoords) +void SV_SetupNetworkBuffers(qboolean bigcoords) { int i; @@ -741,31 +741,6 @@ static void SV_SetupNetworkBuffers(qboolean bigcoords) sv.num_signon_buffers = 1; } -#ifdef SUBSERVERS -void SV_SpawnClusterMode(void) -{ - char *sqlparams[] = - { - "", - "", - "", - "login", - }; - if (sv.state) - SV_UnspawnServer(); - NET_InitServer(); - - //child processes return 0 and fall through - memset(&sv, 0, sizeof(sv)); - sv.state = ss_clustermode; - sv.logindatabase = -1;//SQL_NewServer("sqlite", sqlparams); - - //and for legacy clients, we need some server stuff inited. - SV_SetupNetworkBuffers(false); - SV_UpdateMaxPlayers(32); -} -#endif - /* ================ SV_SpawnServer diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 5f3409e5a..b8bb9bd25 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -247,7 +247,8 @@ void SV_Shutdown (void) sv_fraglogfile = NULL; } - PR_Shutdown(); + SV_UnspawnServer(); + #ifdef USEODE World_ODE_Shutdown(); #endif @@ -494,11 +495,11 @@ void SV_DropClient (client_t *drop) Rank_SetPlayerStats(drop->rankid, &rs); } } -#endif -#ifdef SUBSERVERS - SSV_SavePlayerStats(drop, false); #endif } +#ifdef SUBSERVERS + SSV_SavePlayerStats(drop, 2); +#endif #ifdef SVCHAT SV_WipeChat(drop); #endif @@ -1854,798 +1855,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) } } -#ifdef SUBSERVERS -void MSV_UpdatePlayerStats(qboolean initial, unsigned int playerid, unsigned int serverid, int numstats, float *stats); -static char *knownmaps[] = -{ - "", - "start" -}; - -static pubsubserver_t *subservers; -qboolean isClusterSlave; -unsigned int nextserverid; - -pubsubserver_t *MSV_StartSubServer(unsigned int id, const char *mapname) -{ - sizebuf_t send; - char send_buf[64]; - pubsubserver_t *s = Sys_ForkServer(); - if (s) - { - if (!id) - { - if (nextserverid < sizeof(knownmaps)/sizeof(knownmaps[0])) - nextserverid = sizeof(knownmaps)/sizeof(knownmaps[0]); - id = nextserverid++; - } - s->id = id; - s->next = subservers; - subservers = s; - - Q_strncpyz(s->name, mapname, sizeof(s->name)); - - memset(&send, 0, sizeof(send)); - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 2; - MSG_WriteByte(&send, ccmd_acceptserver); - MSG_WriteLong(&send, s->id); - MSG_WriteString(&send, s->name); - Sys_InstructSlave(s, &send); - } - return s; -} - -pubsubserver_t *MSV_FindSubServer(unsigned int id) -{ - pubsubserver_t *s; - for (s = subservers; s; s = s->next) - { - if (id == s->id) - return s; - } - - if (!s && id >= 1 && id < sizeof(knownmaps)/sizeof(knownmaps[0])) - s = MSV_StartSubServer(id, knownmaps[id]); - - return s; -} -pubsubserver_t *MSV_FindSubServerName(const char *mapname) -{ - pubsubserver_t *s; - unsigned int to; - for (to = 1; to < sizeof(knownmaps)/sizeof(knownmaps[0]); to++) - { - if (!strcmp(knownmaps[to], mapname)) - return MSV_FindSubServer(to); - } - - for (s = subservers; s; s = s->next) - { - if (!strcmp(s->name, mapname)) - return s; - } - - return MSV_StartSubServer(0, mapname); -} -qboolean MSV_AddressForMap(netadr_t *ret, int natype, int serverid) -{ - pubsubserver_t *s = MSV_FindSubServer(serverid); - - if (s) - { - if (natype == s->addrv6.type) - *ret = s->addrv6; - else - *ret = s->addrv4; - return true; - } - return false; -} - -void MSV_InstructSlave(unsigned int id, sizebuf_t *cmd) -{ - pubsubserver_t *s; - if (!id) - { - for (s = subservers; s; s = s->next) - Sys_InstructSlave(s, cmd); - } - else - { - s = MSV_FindSubServer(id); - if (s) - Sys_InstructSlave(s, cmd); - } -} - -void MSV_MapCluster_f(void) -{ - SV_SpawnClusterMode(); -} - -void SSV_PrintToMaster(char *s) -{ - sizebuf_t send; - char send_buf[8192]; - memset(&send, 0, sizeof(send)); - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 2; - - MSG_WriteByte(&send, ccmd_print); - MSG_WriteString(&send, s); - SSV_InstructMaster(&send); -} - -void MSV_SubServerCommand_f(void) -{ - sizebuf_t buf; - char bufmem[1024]; - pubsubserver_t *s; - int id; - char *c; - if (Cmd_Argc() == 1) - { - Con_Printf("Active servers on this cluster:\n"); - for (s = subservers; s; s = s->next) - { - Con_Printf("%i: %s", s->id, s->name); - if (s->addrv4.type != NA_INVALID) - Con_Printf(" %s", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv4)); - if (s->addrv6.type != NA_INVALID) - Con_Printf(" %s", NET_AdrToString(bufmem, sizeof(bufmem), &s->addrv6)); - Con_Printf("\n"); - } - return; - } - if (!strcmp(Cmd_Argv(0), "ssv_all")) - id = 0; - else - { - id = atoi(Cmd_Argv(1)); - Cmd_ShiftArgs(1, false); - } - - buf.data = bufmem; - buf.maxsize = sizeof(bufmem); - buf.cursize = 2; - buf.packing = SZ_RAWBYTES; - c = Cmd_Args(); - MSG_WriteByte(&buf, ccmd_stuffcmd); - MSG_WriteString(&buf, c); - buf.data[0] = buf.cursize & 0xff; - buf.data[1] = (buf.cursize>>8) & 0xff; - MSV_InstructSlave(id, &buf); -} - -void MSV_ReadFromSubServer(pubsubserver_t *s) -{ - sizebuf_t send; - qbyte send_buf[MAX_QWMSGLEN]; - netadr_t adr; - char *str; - int c; - - c = MSG_ReadByte(); - switch(c) - { - default: - case ccmd_bad: - Sys_Error("Corrupt message (%i) from SubServer %i:%s", c, s->id, s->name); - break; - case ccmd_print: - Con_Printf("%s", MSG_ReadString()); - break; - case ccmd_saveplayer: - { - float stats[NUM_SPAWN_PARMS]; - int i, numstats; - unsigned int lastserver = MSG_ReadLong(); - int plid = MSG_ReadLong(); - numstats = MSG_ReadByte(); - for (i = 0; i < numstats; i++) - stats[i] = MSG_ReadFloat(); - MSV_UpdatePlayerStats(lastserver, plid, s->id, numstats, stats); - } - break; - case ccmd_transferplayer: - { - char guid[64]; - char mapname[64]; - int plid = MSG_ReadLong(); - char *newmap = MSG_ReadStringBuffer(mapname, sizeof(mapname)); - char *claddr = MSG_ReadString(); - char *clguid = MSG_ReadStringBuffer(guid, sizeof(guid)); - pubsubserver_t *toptr; - - memset(&send, 0, sizeof(send)); - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 2; - - if (NULL!=(toptr=MSV_FindSubServerName(newmap))) - { - Con_Printf("Transfer to %i:%s\n", toptr->id, toptr->name); - - MSG_WriteByte(&send, ccmd_takeplayer); - MSG_WriteLong(&send, plid); - MSG_WriteLong(&send, s->id); - MSG_WriteString(&send, claddr); - MSG_WriteString(&send, clguid); - - c = MSG_ReadByte(); - MSG_WriteByte(&send, c); - Con_Printf("Transfer %i stats\n", c); - while(c--) - MSG_WriteFloat(&send, MSG_ReadFloat()); - - Sys_InstructSlave(toptr, &send); - } - else - { - //suck up the stats - c = MSG_ReadByte(); - while(c--) - MSG_ReadFloat(); - - Con_Printf("Transfer abort\n"); - - MSG_WriteByte(&send, ccmd_tookplayer); - MSG_WriteLong(&send, s->id); - MSG_WriteLong(&send, plid); - MSG_WriteString(&send, ""); - - Sys_InstructSlave(s, &send); - } - } - break; - case ccmd_tookplayer: - { - int to = MSG_ReadLong(); - int plid = MSG_ReadLong(); - char *claddr = MSG_ReadString(); - char *rmsg; - netadr_t cladr; - netadr_t svadr; - char adrbuf[256]; - Con_Printf("Took player\n"); - - memset(&send, 0, sizeof(send)); - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 2; - - NET_StringToAdr(claddr, 0, &cladr); - MSV_AddressForMap(&svadr, cladr.type, s->id); - if (!to) - { - if (svadr.type != NA_INVALID) - { - rmsg = va("fredir\n%s", NET_AdrToString(adrbuf, sizeof(adrbuf), &svadr)); - Netchan_OutOfBand (NS_SERVER, &cladr, strlen(rmsg), (qbyte *)rmsg); - } - } - else - { - MSG_WriteByte(&send, ccmd_tookplayer); - MSG_WriteLong(&send, s->id); - MSG_WriteLong(&send, plid); - MSG_WriteString(&send, NET_AdrToString(adrbuf, sizeof(adrbuf), &svadr)); - - MSV_InstructSlave(to, &send); - } - } - break; - case ccmd_serveraddress: - s->addrv4.type = NA_INVALID; - s->addrv6.type = NA_INVALID; - str = MSG_ReadString(); - Q_strncpyz(s->name, str, sizeof(s->name)); - for (;;) - { - str = MSG_ReadString(); - if (!*str) - break; - if (NET_StringToAdr(str, 0, &adr)) - { - if (adr.type == NA_IP && s->addrv4.type == NA_INVALID) - s->addrv4 = adr; - if (adr.type == NA_IPV6 && s->addrv6.type == NA_INVALID) - s->addrv6 = adr; - } - } - Con_Printf("%i:%s: restarted\n", s->id, s->name); - break; - } - if (msg_readcount != net_message.cursize || msg_badread) - Sys_Error("Master: Readcount isn't right (%i)\n", net_message.data[0]); -} - -void MSV_PollSlaves(void) -{ - pubsubserver_t **link, *s; - for (link = &subservers; (s=*link); ) - { - switch(Sys_SubServerRead(s)) - { - case -1: - //error - server is dead and needs to be freed. - *link = s->next; - Z_Free(s); - break; - case 0: - //no messages - link = &s->next; - break; - case 1: - //got a message. read it and see if there's more. - MSV_ReadFromSubServer(s); - break; - } - } -} - -void SSV_ReadFromControlServer(void) -{ - int c; - char *s; - - c = MSG_ReadByte(); - switch(c) - { - case ccmd_bad: - default: - SV_Error("Invalid message from cluster (%i)\n", c); - break; - case ccmd_stuffcmd: - s = MSG_ReadString(); - SV_BeginRedirect(RD_MASTER, 0); - Cmd_ExecuteString(s, RESTRICT_LOCAL); - SV_EndRedirect(); - break; - - case ccmd_acceptserver: - svs.clusterserverid = MSG_ReadLong(); - s = MSG_ReadString(); - if (*s && !strchr(s, ';') && !strchr(s, '\n') && !strchr(s, '\"')) //sanity check the argument - Cmd_ExecuteString(va("map \"%s\"", s), RESTRICT_LOCAL); - if (svprogfuncs && pr_global_ptrs->serverid) - *pr_global_ptrs->serverid = svs.clusterserverid; - break; - - case ccmd_tookplayer: - { - client_t *cl = NULL; - int to = MSG_ReadLong(); - int plid = MSG_ReadLong(); - char *addr = MSG_ReadString(); - int i; - - Con_Printf("%s: got tookplayer\n", sv.name); - - for (i = 0; i < svs.allocated_client_slots; i++) - { - if (svs.clients[i].state && svs.clients[i].userid == plid) - { - cl = &svs.clients[i]; - break; - } - } - if (cl) - { - if (!*addr) - { - Con_Printf("%s: tookplayer: failed\n", sv.name); - Info_SetValueForStarKey(cl->userinfo, "*transfer", "", sizeof(cl->userinfo)); - } - else - { - Con_Printf("%s: tookplayer: do transfer\n", sv.name); -// SV_StuffcmdToClient(cl, va("connect \"%s\"\n", addr)); - SV_StuffcmdToClient(cl, va("cl_transfer \"%s\"\n", addr)); - cl->redirect = 2; - } - } - else - Con_Printf("%s: tookplayer: invalid player.\n", sv.name); - } - break; - - case ccmd_transferedplayer: - { - client_t *cl; - char *to; - int toserver = MSG_ReadLong(); - int playerid = MSG_ReadLong(); - int i; - - for (i = 0; i < svs.allocated_client_slots; i++) - { - if (svs.clients[i].userid == playerid && svs.clients[i].state >= cs_loadzombie) - { - cl = &svs.clients[i]; - cl->drop = true; - to = Info_ValueForKey(cl->userinfo, "*transfer"); - Con_Printf("%s transfered to %s\n", cl->name, to); - break; - } - } - } - break; - - case ccmd_takeplayer: - { - client_t *cl = NULL; - int i, j; - float stat; - char guid[64]; - int plid = MSG_ReadLong(); - int fromsv = MSG_ReadLong(); - char *claddr = MSG_ReadString(); - char *clguid = MSG_ReadStringBuffer(guid, sizeof(guid)); - - if (sv.state >= ss_active) - { - for (i = 0; i < svs.allocated_client_slots; i++) - { - if (!svs.clients[i].state || (svs.clients[i].userid == plid && svs.clients[i].state >= cs_loadzombie)) - { - cl = &svs.clients[i]; - break; - } - } - } - - Con_Printf("%s: takeplayer\n", sv.name); - if (cl) - { - cl->userid = plid; - if (cl->state == cs_loadzombie && cl->istobeloaded) - cl->connection_started = realtime+20; //renew the slot - else if (!cl->state) - { //allocate a new pending player. - cl->previousserver = fromsv; - cl->state = cs_loadzombie; - cl->connection_started = realtime+20; - Q_strncpyz(cl->guid, clguid, sizeof(cl->guid)); - sv.spawned_client_slots++; - memset(&cl->netchan, 0, sizeof(cl->netchan)); - SV_GetNewSpawnParms(cl); - } - } - - j = MSG_ReadByte(); - Con_Printf("%s: %i stats\n", sv.name, j); - for (i = 0; i < j; i++) - { - stat = MSG_ReadFloat(); - if (cl && cl->state == cs_loadzombie && i < NUM_SPAWN_PARMS) - cl->spawn_parms[i] = stat; - } - - { - sizebuf_t send; - qbyte send_buf[MAX_QWMSGLEN]; - - if (fromsv) - Con_Printf("%s: send tookplayer\n", sv.name); - else - Con_Printf("%s: from master\n", sv.name); - memset(&send, 0, sizeof(send)); - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 2; - - if (cl) - { - MSG_WriteByte(&send, ccmd_tookplayer); - MSG_WriteLong(&send, fromsv); - MSG_WriteLong(&send, plid); - MSG_WriteString(&send, claddr); - SSV_InstructMaster(&send); - } - } - } - break; - } - - if (msg_readcount != net_message.cursize || msg_badread) - Sys_Error("Subserver: Readcount isn't right (%i)\n", net_message.data[0]); -} - -void SSV_UpdateAddresses(void) -{ - char buf[256]; - netadr_t addr[64]; - struct ftenet_generic_connection_s *con[sizeof(addr)/sizeof(addr[0])]; - int flags[sizeof(addr)/sizeof(addr[0])]; - int count; - sizebuf_t send; - qbyte send_buf[MAX_QWMSGLEN]; - int i; - - if (!SSV_IsSubServer()) - return; - - count = NET_EnumerateAddresses(svs.sockets, con, flags, addr, sizeof(addr)/sizeof(addr[0])); - - if (*sv_serverip.string) - { - for (i = 0; i < count; i++) - { - if (addr[i].type == NA_IP) - { - NET_StringToAdr(sv_serverip.string, BigShort(addr[i].port), &addr[0]); - count = 1; - break; - } - } - } - - memset(&send, 0, sizeof(send)); - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 2; - MSG_WriteByte(&send, ccmd_serveraddress); - - MSG_WriteString(&send, sv.name); - for (i = 0; i < count; i++) - MSG_WriteString(&send, NET_AdrToString(buf, sizeof(buf), &addr[i])); - MSG_WriteByte(&send, 0); - SSV_InstructMaster(&send); -} - -void SSV_SavePlayerStats(client_t *cl, unsigned int previousserver) -{ - //called when the *transfer userinfo gets set to the new map - sizebuf_t send; - qbyte send_buf[MAX_QWMSGLEN]; - int i; - if (!SSV_IsSubServer()) - return; - - if (!previousserver) - SV_SaveSpawnparmsClient(cl, NULL); - - memset(&send, 0, sizeof(send)); - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 2; - - MSG_WriteByte(&send, ccmd_saveplayer); - MSG_WriteLong(&send, previousserver); - MSG_WriteLong(&send, cl->userid); - MSG_WriteByte(&send, NUM_SPAWN_PARMS); - for (i = 0; i < NUM_SPAWN_PARMS; i++) - { - MSG_WriteFloat(&send, cl->spawn_parms[i]); - } - - SSV_InstructMaster(&send); -} -void SSV_InitiatePlayerTransfer(client_t *cl, const char *newserver) -{ - //called when the *transfer userinfo gets set to the new map - sizebuf_t send; - qbyte send_buf[MAX_QWMSGLEN]; - int i; - char tmpbuf[256]; - float parms[NUM_SPAWN_PARMS]; - - SV_SaveSpawnparmsClient(cl, parms); - - memset(&send, 0, sizeof(send)); - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 2; - MSG_WriteByte(&send, ccmd_transferplayer); - MSG_WriteLong(&send, cl->userid); - MSG_WriteString(&send, newserver); - MSG_WriteString(&send, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &cl->netchan.remote_address)); - MSG_WriteString(&send, cl->guid); - - //stats - MSG_WriteByte(&send, NUM_SPAWN_PARMS); - for (i = 0; i < NUM_SPAWN_PARMS; i++) - { - MSG_WriteFloat(&send, parms[i]); - } - - SSV_InstructMaster(&send); -} - -#ifdef SQL -#include "sv_sql.h" -int pendinglookups = 0; -struct logininfo_s -{ - netadr_t clientaddr; - char guid[64]; -}; -#endif -qboolean SV_IgnoreSQLResult(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof) -{ - return false; -} -void MSV_UpdatePlayerStats(unsigned int lastserver, unsigned int playerid, unsigned int serverid, int numstats, float *stats) -{ - queryrequest_t *req; - sqlserver_t *srv; - static char hex[16] = "0123456789abcdef"; - char sql[2048], *sqle; - union{float *f;qbyte *b;} blob; - Q_snprintfz(sql, sizeof(sql), "UPDATE accounts SET stats=x'"); - sqle = sql+strlen(sql); - for (blob.f = stats, numstats*=4; numstats--; blob.b++) - { - *sqle++ = hex[*blob.b>>4]; - *sqle++ = hex[*blob.b&15]; - } - if (lastserver) - Q_snprintfz(sqle, sizeof(sql)-(sqle-sql), "', serverid=%u WHERE playerid = %u;", serverid, playerid); - else - Q_snprintfz(sqle, sizeof(sql)-(sqle-sql), "' WHERE playerid = %u AND serverid = %u;", playerid, serverid); - - srv = SQL_GetServer(sv.logindatabase, false); - if (srv) - SQL_NewQuery(srv, SV_IgnoreSQLResult, sql, &req); - - if (lastserver) - { - sizebuf_t send; - qbyte send_buf[64]; - memset(&send, 0, sizeof(send)); - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 2; - MSG_WriteByte(&send, ccmd_transferedplayer); - MSG_WriteLong(&send, serverid); - MSG_WriteLong(&send, playerid); - MSV_InstructSlave(lastserver, &send); - } -} - -qboolean MSV_ClusterLoginReply(netadr_t *legacyclientredirect, unsigned int serverid, unsigned int playerid, char *clientguid, netadr_t *clientaddr, void *statsblob, size_t statsblobsize) -{ - char tmpbuf[256]; - netadr_t serveraddr; - - if (!serverid) - serverid = 1; - - if (!MSV_AddressForMap(&serveraddr, clientaddr->type, serverid) && !MSV_AddressForMap(&serveraddr, clientaddr->type, serverid=1)) - SV_RejectMessage(SCP_QUAKEWORLD, "Unable to find lobby.\n"); - else - { - sizebuf_t send; - qbyte send_buf[MAX_QWMSGLEN]; - memset(&send, 0, sizeof(send)); - send.data = send_buf; - send.maxsize = sizeof(send_buf); - send.cursize = 2; - MSG_WriteByte(&send, ccmd_takeplayer); - MSG_WriteLong(&send, playerid); - MSG_WriteLong(&send, 0); //from server - MSG_WriteString(&send, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &net_from)); - MSG_WriteString(&send, clientguid); - - MSG_WriteByte(&send, statsblobsize/4); - SZ_Write(&send, statsblob, statsblobsize&~3); - MSV_InstructSlave(serverid, &send); - - if (serveraddr.type == NA_INVALID) - { - if (net_from.type != NA_LOOPBACK) - SV_RejectMessage(SCP_QUAKEWORLD, "Starting instance.\n"); - } - else if (legacyclientredirect) - { - *legacyclientredirect = serveraddr; - return true; - } - else - { - char *s = va("fredir\n%s", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &serveraddr)); - Netchan_OutOfBand (NS_SERVER, clientaddr, strlen(s), (qbyte *)s); - return true; - } - } - return false; -} - -qboolean MSV_ClusterLoginSQLResult(queryrequest_t *req, int firstrow, int numrows, int numcols, qboolean eof) -{ - sqlserver_t *sql = SQL_GetServer(req->srvid, true); - queryresult_t *res = SQL_GetQueryResult(sql, req->num, 0); - struct logininfo_s *info = req->user.thread; - char *s; - int playerid, serverid; - char *statsblob; - size_t blobsize; - - res = SQL_GetQueryResult(sql, req->num, 0); - if (!res) - { - playerid = 0; - statsblob = NULL; - blobsize = 0; - serverid = 0; - } - else - { - s = SQL_ReadField(sql, res, 0, 0, true, NULL); - playerid = atoi(s); - - statsblob = SQL_ReadField(sql, res, 0, 2, true, &blobsize); - - s = SQL_ReadField(sql, res, 0, 1, true, NULL); - serverid = s?atoi(s):0; - } - - net_from = info->clientaddr; //okay, that's a bit stupid, rewrite rejectmessage to accept an arg? - if (!playerid) - SV_RejectMessage(SCP_QUAKEWORLD, "Bad username or password.\n"); - else - MSV_ClusterLoginReply(NULL, serverid, playerid, info->guid, &info->clientaddr, statsblob, blobsize); - Z_Free(info); - pendinglookups--; - return false; -} - -//returns true to block entry to this server. -qboolean MSV_ClusterLogin(char *guid, char *userinfo, size_t userinfosize) -{ - char escname[64], escpasswd[64]; - sqlserver_t *sql; - queryrequest_t *req; - struct logininfo_s *info; - char *sqlparams[] = - { - "", - "", - "", - "login", - }; - - if (sv.state != ss_clustermode) - return false; - - if (sv.logindatabase != -1) - { - if (pendinglookups > 10) - return true; - sql = SQL_GetServer(sv.logindatabase, false); - if (!sql) - return true; - SQL_Escape(sql, Info_ValueForKey(userinfo, "name"), escname, sizeof(escname)); - SQL_Escape(sql, Info_ValueForKey(userinfo, "password"), escpasswd, sizeof(escpasswd)); - if (SQL_NewQuery(sql, MSV_ClusterLoginSQLResult, va("SELECT playerid,serverid,stats FROM accounts WHERE name='%s' AND password='%s';", escname, escpasswd), &req) != -1) - { - pendinglookups++; - req->user.thread = info = Z_Malloc(sizeof(*info)); - Q_strncpyz(info->guid, guid, sizeof(info->guid)); - info->clientaddr = net_from; - } - } - else if (0) - { - char tmpbuf[256]; - netadr_t redir; - if (MSV_ClusterLoginReply(&redir, 0, nextuserid++, guid, &net_from, NULL, 0)) - { - Info_SetValueForStarKey(userinfo, "*redirect", NET_AdrToString(tmpbuf, sizeof(tmpbuf), &redir), userinfosize); - return false; - } - return true; - } - else - MSV_ClusterLoginReply(NULL, 0, nextuserid++, guid, &net_from, NULL, 0); - return true; -} -#endif /* ================== @@ -3045,7 +2255,7 @@ client_t *SVC_DirectConnect(void) // if there is already a slot for this ip, drop it for (i=0,cl=svs.clients ; istate == cs_free) + if (cl->state == cs_free || cl->state == cs_loadzombie) continue; if (NET_CompareBaseAdr (&adr, &cl->netchan.remote_address) && ( cl->netchan.qport == qport @@ -3142,9 +2352,8 @@ client_t *SVC_DirectConnect(void) preserveparms = true; temp.istobeloaded = cl->istobeloaded; memcpy(temp.spawn_parms, cl->spawn_parms, sizeof(temp.spawn_parms)); -#ifdef SUBSERVERS - temp.previousserver = cl->previousserver; -#endif + if (cl->userid) + temp.userid = cl->userid; break; } } @@ -3643,8 +2852,7 @@ client_t *SVC_DirectConnect(void) newcl->redirect = redirect; #ifdef SUBSERVERS - SSV_SavePlayerStats(newcl, newcl->previousserver); - newcl->previousserver = 0; + SSV_SavePlayerStats(newcl, 0); #endif return newcl; @@ -4043,7 +3251,7 @@ qboolean SVNQ_ConnectionlessPacket(void) if (net_from.type == NA_LOOPBACK) return false; - if (!sv_listen_nq.value) + if (!sv_listen_nq.value || SSV_IsSubServer()) return false; MSG_BeginReading(svs.netprim); @@ -4681,7 +3889,7 @@ void SV_CheckTimeouts (void) if (*cl->name) SV_BroadcastTPrintf (PRINT_HIGH, "LoadZombie %s timed out\n", cl->name); else - SV_BroadcastTPrintf (PRINT_HIGH, "LoadZombie timed out\n", cl->name); + SV_BroadcastTPrintf (PRINT_HIGH, "LoadZombie timed out\n"); } sv.spawned_client_slots--; @@ -4694,7 +3902,11 @@ void SV_CheckTimeouts (void) else { //no entity, just free them. +#ifdef SUBSERVERS + SSV_SavePlayerStats(cl, 3); +#endif cl->state = cs_free; + SV_BroadcastTPrintf (PRINT_HIGH, "TransferZombie %s timed out\n", cl->name); } cl->netchan.remote_address.type = NA_INVALID; //don't mess up from not knowing their address. } @@ -5397,7 +4609,7 @@ void Master_Heartbeat (void) qboolean madeqwstring = false; char adr[MAX_ADR_SIZE]; - if (!sv_public.ival) + if (!sv_public.ival || SSV_IsSubServer()) return; if (realtime-HEARTBEAT_SECONDS - svs.last_heartbeat < HEARTBEAT_SECONDS) diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 558ea7f21..39d2517bc 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -60,8 +60,6 @@ void SV_FlushRedirect (void) if (!*outputbuf) return; - Log_String(LOG_CONSOLE, va("{\n%s}\n", outputbuf)); - if (sv_redirected == RD_PACKET || sv_redirected == RD_PACKET_LOG) { //log it to the rcon log if its not just a status response diff --git a/engine/server/sv_sys_win.c b/engine/server/sv_sys_win.c index cfa6931b9..e95bfb2c5 100644 --- a/engine/server/sv_sys_win.c +++ b/engine/server/sv_sys_win.c @@ -41,11 +41,9 @@ static HANDLE hconsoleout; qboolean WinNT; //if true, use utf-16 file paths. if false, hope that paths are in ascii. -#ifdef _DEBUG -#if _MSC_VER >= 1300 +#if defined(_DEBUG) || defined(DEBUG) #define CATCHCRASH #endif -#endif #ifdef CATCHCRASH @@ -71,18 +69,197 @@ DWORD CrashExceptionHandler (DWORD exceptionCode, LPEXCEPTION_POINTERS exception HMODULE hKernel; BOOL (WINAPI *pIsDebuggerPresent)(void); + DWORD (WINAPI *pSymSetOptions)(DWORD SymOptions); + BOOL (WINAPI *pSymInitialize)(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess); + BOOL (WINAPI *pSymFromAddr)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, PSYMBOL_INFO Symbol); + +#ifdef _WIN64 +#define DBGHELP_POSTFIX "64" + BOOL (WINAPI *pStackWalkX)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME64 StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); + PVOID (WINAPI *pSymFunctionTableAccessX)(HANDLE hProcess, DWORD64 AddrBase); + DWORD64 (WINAPI *pSymGetModuleBaseX)(HANDLE hProcess, DWORD64 qwAddr); + BOOL (WINAPI *pSymGetLineFromAddrX)(HANDLE hProcess, DWORD64 qwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64); + BOOL (WINAPI *pSymGetModuleInfoX)(HANDLE hProcess, DWORD64 qwAddr, PIMAGEHLP_MODULE64 ModuleInfo); + #define STACKFRAMEX STACKFRAME64 + #define IMAGEHLP_LINEX IMAGEHLP_LINE64 + #define IMAGEHLP_MODULEX IMAGEHLP_MODULE64 +#else +#define DBGHELP_POSTFIX "" + BOOL (WINAPI *pStackWalkX)(DWORD MachineType, HANDLE hProcess, HANDLE hThread, LPSTACKFRAME StackFrame, PVOID ContextRecord, PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine, PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine, PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine, PTRANSLATE_ADDRESS_ROUTINE TranslateAddress); + PVOID (WINAPI *pSymFunctionTableAccessX)(HANDLE hProcess, DWORD AddrBase); + DWORD (WINAPI *pSymGetModuleBaseX)(HANDLE hProcess, DWORD dwAddr); + BOOL (WINAPI *pSymGetLineFromAddrX)(HANDLE hProcess, DWORD dwAddr, PDWORD pdwDisplacement, PIMAGEHLP_LINE Line); + BOOL (WINAPI *pSymGetModuleInfoX)(HANDLE hProcess, DWORD dwAddr, PIMAGEHLP_MODULE ModuleInfo); + #define STACKFRAMEX STACKFRAME + #define IMAGEHLP_LINEX IMAGEHLP_LINE + #define IMAGEHLP_MODULEX IMAGEHLP_MODULE +#endif + dllfunction_t debughelpfuncs[] = + { + {(void*)&pSymFromAddr, "SymFromAddr"}, + {(void*)&pSymSetOptions, "SymSetOptions"}, + {(void*)&pSymInitialize, "SymInitialize"}, + {(void*)&pStackWalkX, "StackWalk"DBGHELP_POSTFIX}, + {(void*)&pSymFunctionTableAccessX, "SymFunctionTableAccess"DBGHELP_POSTFIX}, + {(void*)&pSymGetModuleBaseX, "SymGetModuleBase"DBGHELP_POSTFIX}, + {(void*)&pSymGetLineFromAddrX, "SymGetLineFromAddr"DBGHELP_POSTFIX}, + {(void*)&pSymGetModuleInfoX, "SymGetModuleInfo"DBGHELP_POSTFIX}, + {NULL, NULL} + }; + + switch(exceptionCode) + { + case EXCEPTION_ACCESS_VIOLATION: + case EXCEPTION_DATATYPE_MISALIGNMENT: + case EXCEPTION_BREAKPOINT: + case EXCEPTION_SINGLE_STEP: + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + case EXCEPTION_FLT_DENORMAL_OPERAND: + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + case EXCEPTION_FLT_INEXACT_RESULT: + case EXCEPTION_FLT_INVALID_OPERATION: + case EXCEPTION_FLT_OVERFLOW: + case EXCEPTION_FLT_STACK_CHECK: + case EXCEPTION_FLT_UNDERFLOW: + case EXCEPTION_INT_DIVIDE_BY_ZERO: + case EXCEPTION_INT_OVERFLOW: + case EXCEPTION_PRIV_INSTRUCTION: + case EXCEPTION_IN_PAGE_ERROR: + case EXCEPTION_ILLEGAL_INSTRUCTION: + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + case EXCEPTION_STACK_OVERFLOW: + case EXCEPTION_INVALID_DISPOSITION: + case EXCEPTION_GUARD_PAGE: + case EXCEPTION_INVALID_HANDLE: +// case EXCEPTION_POSSIBLE_DEADLOCK: + break; + default: + //because windows is a steaming pile of shite, we have to ignore any software-generated exceptions, because most of them are not in fact fatal, *EVEN IF THEY CLAIM TO BE NON-CONTINUABLE* + return exceptionCode; + } + hKernel = LoadLibrary ("kernel32"); pIsDebuggerPresent = (void*)GetProcAddress(hKernel, "IsDebuggerPresent"); + if (pIsDebuggerPresent && pIsDebuggerPresent()) + return EXCEPTION_CONTINUE_SEARCH; #ifdef GLQUAKE GLVID_Crashed(); #endif - if (pIsDebuggerPresent ()) +#if 1//ndef _MSC_VER { - /*if we have a current window, minimize it to bring us out of fullscreen*/ - return EXCEPTION_CONTINUE_SEARCH; + if (Sys_LoadLibrary("DBGHELP", debughelpfuncs)) + { + STACKFRAMEX stack; + CONTEXT *pcontext = exceptionInfo->ContextRecord; + IMAGEHLP_LINEX line; + IMAGEHLP_MODULEX module; + struct + { + SYMBOL_INFO sym; + char name[1024]; + } sym; + int frameno; + char stacklog[8192]; + int logpos, logstart; + char *logline; + + stacklog[logpos=0] = 0; + + pSymInitialize(hProc, NULL, TRUE); + pSymSetOptions(SYMOPT_LOAD_LINES); + + memset(&stack, 0, sizeof(stack)); +#ifdef _WIN64 + #define IMAGE_FILE_MACHINE_THIS IMAGE_FILE_MACHINE_AMD64 + stack.AddrPC.Mode = AddrModeFlat; + stack.AddrPC.Offset = pcontext->Rip; + stack.AddrFrame.Mode = AddrModeFlat; + stack.AddrFrame.Offset = pcontext->Rbp; + stack.AddrStack.Mode = AddrModeFlat; + stack.AddrStack.Offset = pcontext->Rsp; +#else + #define IMAGE_FILE_MACHINE_THIS IMAGE_FILE_MACHINE_I386 + stack.AddrPC.Mode = AddrModeFlat; + stack.AddrPC.Offset = pcontext->Eip; + stack.AddrFrame.Mode = AddrModeFlat; + stack.AddrFrame.Offset = pcontext->Ebp; + stack.AddrStack.Mode = AddrModeFlat; + stack.AddrStack.Offset = pcontext->Esp; +#endif + + Q_strncpyz(stacklog+logpos, FULLENGINENAME " or dependancy has crashed. The following stack dump been copied to your windows clipboard.\n" +#ifdef _MSC_VER + "Would you like to generate a core dump too?\n" +#endif + "\n", sizeof(stacklog)-logpos); + logstart = logpos += strlen(stacklog+logpos); + + //so I know which one it is +#if defined(DEBUG) || defined(_DEBUG) + #define BUILDDEBUGREL "Debug" +#else + #define BUILDDEBUGREL "Optimised" +#endif +#ifdef MINIMAL + #define BUILDMINIMAL "Min" +#else + #define BUILDMINIMAL "" +#endif +#if defined(GLQUAKE) && !defined(D3DQUAKE) + #define BUILDTYPE "GL" +#elif !defined(GLQUAKE) && defined(D3DQUAKE) + #define BUILDTYPE "D3D" +#else + #define BUILDTYPE "Merged" +#endif + + Q_snprintfz(stacklog+logpos, sizeof(stacklog)-logpos, "Build: %s %s %s: %s\r\n", BUILDDEBUGREL, PLATFORM, BUILDMINIMAL BUILDTYPE, version_string()); + logpos += strlen(stacklog+logpos); + + for(frameno = 0; ; frameno++) + { + DWORD64 symdisp; + DWORD linedisp; + DWORD_PTR symaddr; + if (!pStackWalkX(IMAGE_FILE_MACHINE_THIS, hProc, GetCurrentThread(), &stack, pcontext, NULL, pSymFunctionTableAccessX, pSymGetModuleBaseX, NULL)) + break; + memset(&module, 0, sizeof(module)); + module.SizeOfStruct = sizeof(module); + pSymGetModuleInfoX(hProc, stack.AddrPC.Offset, &module); + memset(&line, 0, sizeof(line)); + line.SizeOfStruct = sizeof(line); + symdisp = 0; + memset(&sym, 0, sizeof(sym)); + sym.sym.MaxNameLen = sizeof(sym.name); + symaddr = stack.AddrPC.Offset; + sym.sym.SizeOfStruct = sizeof(sym.sym); + if (pSymFromAddr(hProc, symaddr, &symdisp, &sym.sym)) + { + if (pSymGetLineFromAddrX(hProc, stack.AddrPC.Offset, &linedisp, &line)) + logline = va("%-20s - %s:%i (%s)\r\n", sym.sym.Name, line.FileName, (int)line.LineNumber, module.LoadedImageName); + else + logline = va("%-20s+%#x (%s)\r\n", sym.sym.Name, (unsigned int)symdisp, module.LoadedImageName); + } + else + logline = va("0x%p (%s)\r\n", (void*)(DWORD_PTR)stack.AddrPC.Offset, module.LoadedImageName); + Q_strncpyz(stacklog+logpos, logline, sizeof(stacklog)-logpos); + logpos += strlen(stacklog+logpos); + if (logpos+1 >= sizeof(stacklog)) + break; + } + Sys_Printf("%s", stacklog+logstart); + return EXCEPTION_EXECUTE_HANDLER; + } + else + { + Sys_Printf("We crashed.\nUnable to load dbghelp library. Stack info is not available\n"); + return EXCEPTION_EXECUTE_HANDLER; + } } +#endif + hDbgHelp = LoadLibrary ("DBGHELP"); if (hDbgHelp) @@ -124,6 +301,16 @@ DWORD CrashExceptionHandler (DWORD exceptionCode, LPEXCEPTION_POINTERS exception MessageBox(NULL, "Kaboom! Sorry. No MiniDumpWriteDump function.", DISTRIBUTION " Sucks", 0); return EXCEPTION_EXECUTE_HANDLER; } + +LONG CALLBACK nonmsvc_CrashExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) +{ + DWORD foo = EXCEPTION_CONTINUE_SEARCH; + foo = CrashExceptionHandler(/*false, */ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo); + //we have no handler. thus we handle it by exiting. + if (foo == EXCEPTION_EXECUTE_HANDLER) + exit(1); + return foo; +} #endif @@ -584,7 +771,10 @@ void Sys_Error (const char *error, ...) #endif if (COM_CheckParm("-noreset")) + { Sys_Quit(); + exit(1); + } Sys_Printf ("A new server will be started in 10 seconds unless you press a key\n"); @@ -595,7 +785,10 @@ void Sys_Error (const char *error, ...) { Sleep(500); // don't burn up CPU with polling if (_kbhit()) + { Sys_Quit(); + exit(1); + } } Sys_Printf("\nLoading new instance of FTE...\n\n\n"); @@ -1332,18 +1525,26 @@ SERVICE_TABLE_ENTRY DispatchTable[] = int main (int argc, char **argv) { -#ifdef USESERVICE - if (StartServiceCtrlDispatcher( DispatchTable)) - { - return true; - } -#endif - #ifdef CATCHCRASH +#ifdef _MSC_VER __try +#else + AddVectoredExceptionHandler(true, nonmsvc_CrashExceptionHandler); +#endif #endif { COM_InitArgv (argc, (const char **)argv); + +#ifdef SUBSERVERS + isClusterSlave = COM_CheckParm("-clusterslave"); +#endif +#ifdef USESERVICE + if (!SSV_IsSubServer() && StartServiceCtrlDispatcher( DispatchTable)) + { + return true; + } +#endif + #ifdef USESERVICE if (COM_CheckParm("-register")) { @@ -1366,18 +1567,17 @@ int main (int argc, char **argv) } #endif -#ifdef SUBSERVERS - isClusterSlave = COM_CheckParm("-clusterslave"); -#endif StartQuakeServer(); ServerMainLoop(); } #ifdef CATCHCRASH +#ifdef _MSC_VER __except (CrashExceptionHandler(GetExceptionCode(), GetExceptionInformation())) { return 1; } +#endif #endif return true; diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 9c869b251..db9315289 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -4282,6 +4282,20 @@ void Cmd_Fly_f (void) } } +#ifdef SUBSERVERS +void Cmd_Transfer_f(void) +{ + char *dest = Cmd_Argv(1); + if (!SV_MayCheat()) + { + SV_TPrintToClient(host_client, PRINT_HIGH, "Cheats are not allowed on this server\n"); + return; + } + + SSV_InitiatePlayerTransfer(host_client, dest); +} +#endif + /* ==================== Host_SetPos_f UDC @@ -5186,6 +5200,9 @@ ucmd_t ucmds[] = {"fly", Cmd_Fly_f}, {"notarget", Cmd_Notarget_f}, {"setpos", Cmd_SetPos_f}, +#ifdef SUBSERVERS + {"transfer", Cmd_Transfer_f}, //transfer the player to a different map/server +#endif #ifdef NQPROT {"name", SVNQ_NQInfo_f}, diff --git a/engine/sw/sw_rast.c b/engine/sw/sw_rast.c index 621dbf134..ed0f257b5 100644 --- a/engine/sw/sw_rast.c +++ b/engine/sw/sw_rast.c @@ -1043,6 +1043,9 @@ rendererinfo_t swrendererinfo = SW_VID_DeInit, SW_VID_SwapBuffers, SW_VID_ApplyGammaRamps, + NULL, + NULL, + NULL, SW_VID_SetWindowCaption, SW_VID_GetRGBInfo, diff --git a/engine/web/ftejslib.js b/engine/web/ftejslib.js index 7e48ec507..d3b382803 100644 --- a/engine/web/ftejslib.js +++ b/engine/web/ftejslib.js @@ -9,8 +9,29 @@ mergeInto(LibraryManager.library, f: {} }, + //FIXME: split+merge by \n + emscriptenfte_print : function(msg) + { + FTEC.linebuffer += Pointer_stringify(msg); + for(;;) + { + nl = FTEC.linebuffer.indexOf("\n"); + if (nl == -1) + break; + console.log(FTEC.linebuffer.substring(0, nl)); + FTEC.linebuffer = FTEC.linebuffer.substring(nl+1); + } + }, + emscriptenfte_alert : function(msg) + { + msg = Pointer_stringify(msg); + console.log(msg); + alert(msg); + }, + $FTEC: { + ctxwarned:0, linebuffer:'', w: -1, h: -1, @@ -127,9 +148,20 @@ mergeInto(LibraryManager.library, }); }); } - var ctx = Browser.createContext(Module['canvas'], true, true); - if (!ctx) + if (Module.print === undefined) + Module.print = function(msg){console.log(msg);}; + var ctx = Browser.createContext(Module['canvas'], true, true); + if (ctx == null) + { + var msg = "Unable to set up webgl context.\n\nPlease use a browser that supports it and has it enabled\nYour graphics drivers may also be blacklisted, so try updating those too. woo, might as well update your entire operating system while you're at it.\nIt'll be expensive, but hey, its YOUR money, not mine.\nYou can probably just disable the blacklist, but please don't moan at me when your computer blows up, seriously, make sure those drivers are not too buggy.\nI knew a guy once. True story. Boring, but true.\nYou're probably missing out on something right now. Don't you just hate it when that happens?\nMeh, its probably just tinkertoys, right?\n\nYou know, you could always try Internet Explorer, you never know, hell might have frozen over.\nDon't worry, I wasn't serious.\n\nTum te tum. Did you get it working yet?\nDude, fix it already.\n\nThis message was brought to you by Sleep Deprivation, sponsoring quake since I don't know when"; + if (FTEC.ctxwarned == 0) + { + FTEC.ctxwarned = 1; + console.log(msg); + alert(msg); + } return 0; + } // Browser.setCanvasSize(nw, nh, false); window.onresize = function() @@ -168,26 +200,7 @@ mergeInto(LibraryManager.library, msg = Pointer_stringify(msg); throw 'oh noes! something bad happened in ' + msg + '!'; }, - emscriptenfte_alert : function(msg) - { - msg = Pointer_stringify(msg); - console.log(msg); - alert(msg); - }, - //FIXME: split+merge by \n - emscriptenfte_print : function(msg) - { - FTEC.linebuffer += Pointer_stringify(msg); - for(;;) - { - nl = FTEC.linebuffer.indexOf("\n"); - if (nl == -1) - break; - console.log(FTEC.linebuffer.substring(0, nl)); - FTEC.linebuffer = FTEC.linebuffer.substring(nl+1); - } - }, emscriptenfte_ticks_ms : function() { return Date.now(); @@ -231,7 +244,7 @@ mergeInto(LibraryManager.library, var str = window.localStorage.getItem(name); if (str != null) { - console.log('read file '+name+': ' + str); +// console.log('read file '+name+': ' + str); var len = str.length; var buf = new Uint8Array(len); @@ -289,7 +302,6 @@ mergeInto(LibraryManager.library, delete FTEH.f[name]; f.n = null; emscriptenfte_buf_release(f.h); -console.log('deleted '+name); return 1; } return 0; @@ -313,7 +325,6 @@ console.log('deleted '+name); for (var i = 0; i < len; i++) foo += String.fromCharCode(data[i]); window.localStorage.setItem(b.n, foo); -console.log('saved '+b.n+' persistantly: '+foo); } else console.log('local storage not supported');