diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index cbf4f4ef..0976cbe3 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -2904,7 +2904,7 @@ void CL_AddDecal(shader_t *shader, vec3_t origin, vec3_t up, vec3_t side, vec3_t cl_numstris--; } -void R_AddItemTimer(vec3_t shadoworg, float yaw, float radius, float percent) +void R_AddItemTimer(vec3_t shadoworg, float yaw, float radius, float percent, vec3_t rgb) { vec3_t eang; shader_t *s; @@ -2963,7 +2963,7 @@ void R_AddItemTimer(vec3_t shadoworg, float yaw, float radius, float percent) } ctx.t = t; - Vector4Set(ctx.rgbavalue, 0.1, 0.1, 0.1, percent); + Vector4Set(ctx.rgbavalue, rgb[0], rgb[1], rgb[2], percent); Mod_ClipDecal(cl.worldmodel, shadoworg, ctx.axis[0], ctx.axis[1], ctx.axis[2], radius, 0,0, CL_AddDecal_Callback, &ctx); if (!t->numidx) cl_numstris--; @@ -3781,7 +3781,7 @@ void CL_LinkPacketEntities (void) if (le->sequence != cl.lerpentssequence) continue; } - R_AddItemTimer(timer->origin, cl.time*90 + timer->origin[0] + timer->origin[1] + timer->origin[2], timer->radius, (cl.time - timer->start) / timer->duration); + R_AddItemTimer(timer->origin, cl.time*90 + timer->origin[0] + timer->origin[1] + timer->origin[2], timer->radius, (cl.time - timer->start) / timer->duration, timer->rgb); } } diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 727286e0..e968e5a0 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -245,6 +245,15 @@ static struct qboolean trying; qboolean istransfer; //ignore the user's desired server (don't change connect.adr). netadr_t adr; //address that we're trying to transfer to. FIXME: support multiple resolved addresses, eg both ::1 AND 127.0.0.1 +#ifdef HAVE_DTLS + enum + { + DTLS_DISABLE, + DTLS_TRY, + DTLS_REQUIRE, + DTLS_ACTIVE + } dtlsupgrade; +#endif int mtu; unsigned int compresscrc; int protocol; //nq/qw/q2/q3. guessed based upon server replies @@ -528,8 +537,6 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, int fteprotextsupported=0; int fteprotextsupported2=0; #endif - int clients; - int c; char *a; // JACK: Fixed bug where DNS lookups would cause two connects real fast @@ -585,6 +592,7 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, NET_AdrToString(data, sizeof(data), to); Cvar_ForceSet(&cl_serveraddress, data); +// Info_SetValueForStarKey (cls.userinfo, "*ip", data, MAX_INFO_STRING); if (!NET_IsClientLegal(to)) { @@ -606,29 +614,6 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, if (connectinfo.protocol == CP_QUAKE2 && (connectinfo.subprotocol == PROTOCOL_VERSION_R1Q2 || connectinfo.subprotocol == PROTOCOL_VERSION_Q2PRO)) connectinfo.qport &= 0xff; -// Info_SetValueForStarKey (cls.userinfo, "*ip", NET_AdrToString(adr), MAX_INFO_STRING); - - clients = 1; -/* - if (cl_splitscreen.value && (fteprotextsupported & PEXT_SPLITSCREEN)) - { -// if (adr.type == NA_LOOPBACK) - clients = cl_splitscreen.value+1; -// else -// Con_Printf("Split screens are still under development\n"); - } - - if (clients < 1) - clients = 1; - if (clients > MAX_SPLITS) - clients = MAX_SPLITS; - -#ifdef Q2CLIENT - if (connectinfo.protocol == CP_QUAKE2) //q2 only supports after-connect seats - clients = 1; -#endif -*/ - #ifdef Q3CLIENT if (connectinfo.protocol == CP_QUAKE3) { //q3 requires some very strange things. @@ -639,9 +624,6 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, Q_snprintfz(data, sizeof(data), "%c%c%c%cconnect", 255, 255, 255, 255); - if (clients>1) //splitscreen 'connect' command specifies the number of userinfos sent. - Q_strncatz(data, va("%i", clients), sizeof(data)); - Q_strncatz(data, va(" %i %i %i", connectinfo.subprotocol, connectinfo.qport, connectinfo.challenge), sizeof(data)); //userinfo0 has some twiddles for extensions from other qw engines. @@ -659,8 +641,6 @@ void CL_SendConnectPacket (netadr_t *to, int mtu, Q_strncatz(data, va("\\*z_ext\\%i", SUPPORTED_Z_EXTENSIONS), sizeof(data)); Q_strncatz(data, "\"", sizeof(data)); - for (c = 1; c < clients; c++) - Q_strncatz(data, va(" \"%s\"", cls.userinfo[c]), sizeof(data)); if (connectinfo.protocol == CP_QUAKE2 && connectinfo.subprotocol == PROTOCOL_VERSION_R1Q2) Q_strncatz(data, va(" %d %d", mtu, 1905), sizeof(data)); //mti, sub-sub-version @@ -721,11 +701,6 @@ char *CL_TryingToConnect(void) return cls.servername; } -#ifndef CLIENTONLY -int SV_NewChallenge (void); -client_t *SVC_DirectConnect(void); -#endif - /* ================= CL_CheckForResend @@ -759,6 +734,7 @@ void CL_CheckForResend (void) return; //erk? connectinfo.trying = true; connectinfo.istransfer = false; + connectinfo.adr.prot = NP_DGRAM; NET_InitClient(true); @@ -1015,6 +991,24 @@ void CL_CheckForResend (void) SCR_EndLoadingPlaque(); return; } + +#ifdef HAVE_DTLS + if (connectinfo.dtlsupgrade == DTLS_ACTIVE) + { //if we've already established a dtls connection, stick with it + if (connectinfo.adr.prot == NP_DGRAM) + connectinfo.adr.prot = NP_DTLS; + } + else if (connectinfo.adr.prot == NP_DTLS) + { //dtls connections start out with regular udp, and upgrade to dtls once its established that the server supports it. + connectinfo.dtlsupgrade = DTLS_REQUIRE; + connectinfo.adr.prot = NP_DGRAM; + } + else + { + //hostname didn't specify dtls. upgrade if we're allowed, but don't mandate it. + //connectinfo.dtlsupgrade = DTLS_TRY; + } +#endif } if (!NET_IsClientLegal(&connectinfo.adr)) { @@ -1132,6 +1126,9 @@ void CL_BeginServerConnect(const char *host, int port, qboolean noproxy) if (!port) port = cl_defaultport.value; +#ifdef HAVE_DTLS + NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr); +#endif memset(&connectinfo, 0, sizeof(connectinfo)); connectinfo.trying = true; connectinfo.defaultport = port; @@ -1148,6 +1145,10 @@ void CL_BeginServerReconnect(void) Con_TPrintf ("Connect ignored - dedicated. set a renderer first\n"); return; } +#endif +#ifdef HAVE_DTLS + NET_DTLS_Disconnect(cls.sockets, &connectinfo.adr); + connectinfo.dtlsupgrade = 0; #endif connectinfo.trying = true; connectinfo.istransfer = false; @@ -1553,6 +1554,13 @@ void CL_ClearState (void) BZ_Free(cl.item_name[i]); #endif + while (cl.itemtimers) + { + struct itemtimer_s *t = cl.itemtimers; + cl.itemtimers = t->next; + Z_Free(t); + } + { downloadlist_t *next; while(cl.downloadlist) @@ -1792,6 +1800,8 @@ void CL_Disconnect_f (void) connectinfo.trying = false; + NET_CloseClient(); + (void)CSQC_UnconnectedInit(); } @@ -2792,6 +2802,9 @@ void CL_ConnectionlessPacket (void) static netadr_t lastadr; unsigned int curtime = Sys_Milliseconds(); unsigned long pext = 0, pext2 = 0, huffcrc=0, mtu=0; +#ifdef HAVE_DTLS + qboolean candtls = false; +#endif s = MSG_ReadString (); COM_Parse(s); @@ -2888,6 +2901,7 @@ void CL_ConnectionlessPacket (void) } #else Con_Printf("\nUnable to connect to Quake2\n"); + return; #endif s+=9; } @@ -2940,31 +2954,65 @@ void CL_ConnectionlessPacket (void) for(;;) { - c = MSG_ReadLong (); + int cmd = MSG_ReadLong (); if (msg_badread) break; - if (c == PROTOCOL_VERSION_FTE) - pext = MSG_ReadLong (); - else if (c == PROTOCOL_VERSION_FTE2) - pext2 = MSG_ReadLong (); - else if (c == PROTOCOL_VERSION_FRAGMENT) - mtu = MSG_ReadLong (); - else if (c == PROTOCOL_VERSION_VARLENGTH) + if (cmd == PROTOCOL_VERSION_VARLENGTH) { int len = MSG_ReadLong(); if (len < 0 || len > 8192) break; c = MSG_ReadLong();/*ident*/ - MSG_ReadSkip(len); /*payload*/ + switch(c) + { + default: + MSG_ReadSkip(len); /*payload*/ + break; + } } -#ifdef HUFFNETWORK - else if (c == PROTOCOL_VERSION_HUFFMAN) - huffcrc = MSG_ReadLong (); -#endif - //else if (c == PROTOCOL_VERSION_...) else - MSG_ReadLong (); + { + unsigned int l = MSG_ReadLong(); + switch(cmd) + { + case PROTOCOL_VERSION_FTE: pext = l; break; + case PROTOCOL_VERSION_FTE2: pext2 = l; break; + case PROTOCOL_VERSION_FRAGMENT: mtu = l; break; +#ifdef HAVE_DTLS + case PROTOCOL_VERSION_DTLSUPGRADE: candtls = l; break; //0:not enabled. 1:use if you want. 2:require it. +#endif +#ifdef HUFFNETWORK + case PROTOCOL_VERSION_HUFFMAN: huffcrc = l; break; +#endif + default: + break; + } + } } + +#ifdef HAVE_DTLS + if (candtls && connectinfo.adr.prot == NP_DGRAM && (connectinfo.dtlsupgrade || candtls > 1)) + { + //c2s getchallenge + //s2c c%u\0DTLS=0 + //c2s dtlsconnect %u + //s2c dtlsopened + //c2s DTLS(getchallenge) + //DTLS(etc) + + //server says it can do tls. + char *pkt = va("%c%c%c%cdtlsconnect %i", 255, 255, 255, 255, connectinfo.challenge); + NET_SendPacket (NS_CLIENT, strlen(pkt), pkt, &net_from); + return; + } + if (connectinfo.dtlsupgrade == DTLS_REQUIRE) + { + connectinfo.trying = false; + Con_Printf("Server does not support/allow dtls. not connecting.\n"); + return; + } +#endif + CL_SendConnectPacket (&net_from, mtu, pext, pext2, huffcrc/*, ...*/); return; } @@ -3094,13 +3142,37 @@ void CL_ConnectionlessPacket (void) } #endif - if (c == 'd') //note - this conflicts with qw masters, our browser uses a different socket. + if (c == 'd'/*M2C_MASTER_REPLY*/) { - Con_Printf ("d\n"); - if (cls.demoplayback != DPB_NONE) + s = MSG_ReadString (); + COM_Parse(s); + if (!strcmp(com_token, "tlsopened")) + { //server is letting us know that its now listening for a dtls handshake. +#ifdef HAVE_DTLS + Con_Printf ("dtlsopened\n"); + if (!NET_CompareAdr(&connectinfo.adr, &net_from)) + return; + + connectinfo.dtlsupgrade = DTLS_ACTIVE; + connectinfo.adr.prot = NP_DTLS; + if (!NET_DTLS_Create(cls.sockets, &net_from)) + Con_Printf ("unable to establish dtls route\n"); +#else + Con_Printf ("dtlsopened (unsupported)\n"); +#endif + } + else if (*s != '\n') + { //qw master server list response + Con_Printf ("server ip list\n"); + } + else { - Con_Printf("Disconnect\n"); - CL_Disconnect_f(); + Con_Printf ("d\n"); + if (cls.demoplayback != DPB_NONE) + { + Con_Printf("Disconnect\n"); + CL_Disconnect_f(); + } } return; } @@ -3169,7 +3241,12 @@ client_connect: //fixme: make function CL_SendClientCommand(true, "new"); cls.state = ca_connected; if (cls.netchan.remote_address.type != NA_LOOPBACK) - Con_TPrintf ("Connected.\n"); + { + if (cls.netchan.remote_address.prot == NP_DTLS || cls.netchan.remote_address.prot == NP_TLS || cls.netchan.remote_address.prot == NP_WSS) + Con_TPrintf ("Connected (encrypted).\n"); + else + Con_TPrintf ("Connected (plain-text).\n"); + } #ifdef QUAKESPYAPI allowremotecmd = false; // localid required now for remote cmds #endif @@ -3369,7 +3446,19 @@ void CL_ReadPackets (void) for(;;) { if (!CL_GetMessage()) +#ifndef HAVE_DTLS break; +#else + { + NET_DTLS_Timeouts(cls.sockets); + break; + } + + if (*(int *)net_message.data != -1) + if (NET_DTLS_Decode(cls.sockets)) + if (!net_message.cursize) + continue; +#endif #ifdef NQPROT if (cls.demoplayback == DPB_NETQUAKE) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 57f05343..9b2ea76e 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -6109,7 +6109,7 @@ static void CL_ParseItemTimer(void) atof(Cmd_Argv(2)), atof(Cmd_Argv(3))}; float radius = atof(Cmd_Argv(4)); - //unsigned int rgb = strtoul(Cmd_Argv(5), NULL, 16); + unsigned int rgb = (Cmd_Argc() > 5)?strtoul(Cmd_Argv(5), NULL, 16):0x202020; // char *timername = Cmd_Argv(6); unsigned int entnum = strtoul(Cmd_Argv(7), NULL, 0); struct itemtimer_s *timer; @@ -6139,6 +6139,9 @@ static void CL_ParseItemTimer(void) timer->entnum = entnum; timer->start = cl.time; timer->end = cl.time + timer->duration; + timer->rgb[0] = ((rgb>>16)&0xff)/255.0; + timer->rgb[1] = ((rgb>> 8)&0xff)/255.0; + timer->rgb[2] = ((rgb )&0xff)/255.0; } #ifdef PLUGINS diff --git a/engine/client/client.h b/engine/client/client.h index 5af06e14..31991f19 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -893,6 +893,7 @@ typedef struct float start; float duration; vec3_t origin; + vec3_t rgb; float radius; struct itemtimer_s *next; } *itemtimers; diff --git a/engine/client/in_generic.c b/engine/client/in_generic.c index 25c751af..08165916 100644 --- a/engine/client/in_generic.c +++ b/engine/client/in_generic.c @@ -93,8 +93,8 @@ static cvar_t joy_anglesens[3] = }; static cvar_t joy_movesens[3] = { - CVAR("joyforwardsensitivity", "1.0"), - CVAR("joysidesensitivity", "-1.0"), + CVAR("joyforwardsensitivity", "-1.0"), + CVAR("joysidesensitivity", "1.0"), CVAR("joyupsensitivity", "1.0") }; //comments on threshholds comes from microsoft's xinput docs. diff --git a/engine/client/in_sdl.c b/engine/client/in_sdl.c index 32eeba37..2cdbb109 100644 --- a/engine/client/in_sdl.c +++ b/engine/client/in_sdl.c @@ -166,8 +166,12 @@ static struct sdljoy_s *J_DevId(SDL_JoystickID jid) return NULL; } static void J_ControllerAxis(SDL_JoystickID jid, int axis, int value) -{ - int axismap[] = {0,1,3,4,2,5}; +{ //FIXME: sdlaxis 4 and 5 should trigger K_GP_LEFT_TRIGGER and K_GP_RIGHT_TRIGGER + int axismap[] = { +// SDL_CONTROLLER_AXIS_LEFTX, SDL_CONTROLLER_AXIS_LEFTY, SDL_CONTROLLER_AXIS_RIGHTX, + GPAXIS_LT_RIGHT, GPAXIS_LT_DOWN, GPAXIS_RT_RIGHT, +// SDL_CONTROLLER_AXIS_RIGHTY, SDL_CONTROLLER_AXIS_TRIGGERLEFT,SDL_CONTROLLER_AXIS_TRIGGERRIGHT, + GPAXIS_RT_DOWN, GPAXIS_LT_TRIGGER, GPAXIS_RT_TRIGGER}; struct sdljoy_s *joy = J_DevId(jid); if (joy && axis < sizeof(axismap)/sizeof(axismap[0]) && joy->qdevid != DEVID_UNSET) @@ -186,41 +190,23 @@ static void J_ControllerButton(SDL_JoystickID jid, int button, qboolean pressed) { //controllers have reliable button maps. //but that doesn't meant that fte has specific k_ names for those buttons, but the mapping should be reliable, at least until they get mapped to proper k_ values. - int buttonmap[] = { -#if 0 - //NOTE: DP has specific 'X360' buttons for many of these. of course, its not an exact mapping... - K_X360_A, /*SDL_CONTROLLER_BUTTON_A*/ - K_X360_B, /*SDL_CONTROLLER_BUTTON_B*/ - K_X360_X, /*SDL_CONTROLLER_BUTTON_X*/ - K_X360_Y, /*SDL_CONTROLLER_BUTTON_Y*/ - K_X360_BACK, /*SDL_CONTROLLER_BUTTON_BACK*/ - K_AUX2, /*SDL_CONTROLLER_BUTTON_GUIDE*/ - K_X360_START, /*SDL_CONTROLLER_BUTTON_START*/ - K_X360_LEFT_THUMB, /*SDL_CONTROLLER_BUTTON_LEFTSTICK*/ - K_X360_RIGHT_THUMB, /*SDL_CONTROLLER_BUTTON_RIGHTSTICK*/ - K_X360_LEFT_SHOULDER, /*SDL_CONTROLLER_BUTTON_LEFTSHOULDER*/ - K_X360_RIGHT_SHOULDER, /*SDL_CONTROLLER_BUTTON_RIGHTSHOULDER*/ - K_X360_DPAD_UP, /*SDL_CONTROLLER_BUTTON_DPAD_UP*/ - K_X360_DPAD_DOWN, /*SDL_CONTROLLER_BUTTON_DPAD_DOWN*/ - K_X360_DPAD_LEFT, /*SDL_CONTROLLER_BUTTON_DPAD_LEFT*/ - K_X360_DPAD_RIGHT /*SDL_CONTROLLER_BUTTON_DPAD_RIGHT*/ -#else - K_JOY1, /*SDL_CONTROLLER_BUTTON_A*/ - K_JOY2, /*SDL_CONTROLLER_BUTTON_B*/ - K_JOY3, /*SDL_CONTROLLER_BUTTON_X*/ - K_JOY4, /*SDL_CONTROLLER_BUTTON_Y*/ - K_AUX1, /*SDL_CONTROLLER_BUTTON_BACK*/ - K_AUX2, /*SDL_CONTROLLER_BUTTON_GUIDE*/ - K_AUX3, /*SDL_CONTROLLER_BUTTON_START*/ - K_AUX4, /*SDL_CONTROLLER_BUTTON_LEFTSTICK*/ - K_AUX5, /*SDL_CONTROLLER_BUTTON_RIGHTSTICK*/ - K_AUX6, /*SDL_CONTROLLER_BUTTON_LEFTSHOULDER*/ - K_AUX7, /*SDL_CONTROLLER_BUTTON_RIGHTSHOULDER*/ - K_AUX8, /*SDL_CONTROLLER_BUTTON_DPAD_UP*/ - K_AUX9, /*SDL_CONTROLLER_BUTTON_DPAD_DOWN*/ - K_AUX10, /*SDL_CONTROLLER_BUTTON_DPAD_LEFT*/ - K_AUX11 /*SDL_CONTROLLER_BUTTON_DPAD_RIGHT*/ -#endif + int buttonmap[] = + { + K_GP_A, /*SDL_CONTROLLER_BUTTON_A*/ + K_GP_B, /*SDL_CONTROLLER_BUTTON_B*/ + K_GP_X, /*SDL_CONTROLLER_BUTTON_X*/ + K_GP_Y, /*SDL_CONTROLLER_BUTTON_Y*/ + K_GP_BACK, /*SDL_CONTROLLER_BUTTON_BACK*/ + K_GP_GUIDE, /*SDL_CONTROLLER_BUTTON_GUIDE*/ + K_GP_START, /*SDL_CONTROLLER_BUTTON_START*/ + K_GP_LEFT_THUMB, /*SDL_CONTROLLER_BUTTON_LEFTSTICK*/ + K_GP_RIGHT_THUMB, /*SDL_CONTROLLER_BUTTON_RIGHTSTICK*/ + K_GP_LEFT_SHOULDER, /*SDL_CONTROLLER_BUTTON_LEFTSHOULDER*/ + K_GP_RIGHT_SHOULDER, /*SDL_CONTROLLER_BUTTON_RIGHTSHOULDER*/ + K_GP_DPAD_UP, /*SDL_CONTROLLER_BUTTON_DPAD_UP*/ + K_GP_DPAD_DOWN, /*SDL_CONTROLLER_BUTTON_DPAD_DOWN*/ + K_GP_DPAD_LEFT, /*SDL_CONTROLLER_BUTTON_DPAD_LEFT*/ + K_GP_DPAD_RIGHT /*SDL_CONTROLLER_BUTTON_DPAD_RIGHT*/ }; struct sdljoy_s *joy = J_DevId(jid); @@ -721,10 +707,10 @@ static unsigned int tbl_sdltoquake[] = 0,//K_NUMLOCK, //SDLK_NUMLOCK = 300, K_CAPSLOCK, //SDLK_CAPSLOCK = 301, 0,//K_SCROLLOCK,//SDLK_SCROLLOCK= 302, - K_SHIFT, //SDLK_RSHIFT = 303, - K_SHIFT, //SDLK_LSHIFT = 304, - K_CTRL, //SDLK_RCTRL = 305, - K_CTRL, //SDLK_LCTRL = 306, + K_RSHIFT, //SDLK_RSHIFT = 303, + K_LSHIFT, //SDLK_LSHIFT = 304, + K_RCTRL, //SDLK_RCTRL = 305, + K_LCTRL, //SDLK_LCTRL = 306, K_RALT, //SDLK_RALT = 307, K_LALT, //SDLK_LALT = 308, 0, //SDLK_RMETA = 309, diff --git a/engine/client/in_win.c b/engine/client/in_win.c index 2c074c42..bf96ec4b 100644 --- a/engine/client/in_win.c +++ b/engine/client/in_win.c @@ -357,6 +357,7 @@ static void INS_HideMouse (void) //scan for an unused device id for joysticks (now that something was pressed). static int Joy_AllocateDevID(void) { + extern cvar_t cl_splitscreen; int id = 0, j; for (id = 0; ; id++) { @@ -366,12 +367,17 @@ static int Joy_AllocateDevID(void) break; } if (j == joy_count) + { + if (id > cl_splitscreen.ival && !*cl_splitscreen.string) + cl_splitscreen.ival = id; return id; + } } } #ifdef USINGRAWINPUT static int Mouse_AllocateDevID(void) { + extern cvar_t cl_splitscreen; int id = 0, j; for (id = 0; ; id++) { @@ -381,11 +387,16 @@ static int Mouse_AllocateDevID(void) break; } if (j == rawmicecount) + { + if (id > cl_splitscreen.ival && !*cl_splitscreen.string) + cl_splitscreen.ival = id; return id; + } } } static int Keyboard_AllocateDevID(void) { + extern cvar_t cl_splitscreen; int id = 0, j; for (id = 0; ; id++) { @@ -395,7 +406,11 @@ static int Keyboard_AllocateDevID(void) break; } if (j == rawkbdcount) + { + if (id > cl_splitscreen.ival && !*cl_splitscreen.string) + cl_splitscreen.ival = id; return id; + } } } #endif @@ -1817,26 +1832,27 @@ void INS_Commands (void) static const int xinputjbuttons[] = { - K_UPARROW, //XINPUT_GAMEPAD_DPAD_UP - K_DOWNARROW, //XINPUT_GAMEPAD_DPAD_DOWN - K_LEFTARROW, //XINPUT_GAMEPAD_DPAD_LEFT - K_RIGHTARROW, //XINPUT_GAMEPAD_DPAD_RIGHT - K_AUX5, //XINPUT_GAMEPAD_START - K_AUX6, //XINPUT_GAMEPAD_BACK - K_AUX3, //XINPUT_GAMEPAD_LEFT_THUMB - K_AUX4, //XINPUT_GAMEPAD_RIGHT_THUMB + K_GP_DPAD_UP, + K_GP_DPAD_DOWN, + K_GP_DPAD_LEFT, + K_GP_DPAD_RIGHT, + K_GP_START, + K_GP_BACK, + K_GP_LEFT_THUMB, + K_GP_RIGHT_THUMB, - K_AUX1, //XINPUT_GAMEPAD_LEFT_SHOULDER - K_AUX2, //XINPUT_GAMEPAD_RIGHT_SHOULDER - K_AUX7, //unused - K_AUX8, //unused - K_JOY2, //XINPUT_GAMEPAD_A - K_JOY4, //XINPUT_GAMEPAD_B - K_JOY1, //XINPUT_GAMEPAD_X - K_JOY3, //XINPUT_GAMEPAD_Y + K_GP_LEFT_SHOULDER, + K_GP_RIGHT_SHOULDER, + K_GP_GUIDE, //officially, this is 'reserved' + K_GP_UNKNOWN, //reserved + K_GP_A, + K_GP_B, + K_GP_X, + K_GP_Y, - K_AUX9, //left trigger - K_AUX10 //right trigger + //not part of xinput specs, but appended by us from analog triggers + K_GP_LEFT_TRIGGER, + K_GP_RIGHT_TRIGGER }; static const int mmjbuttons[32] = { @@ -2009,12 +2025,12 @@ qboolean INS_ReadJoystick (struct wjoy_s *joy) if (joy->devid != DEVID_UNSET) { - IN_JoystickAxisEvent(joy->devid, 0, xistate.Gamepad.sThumbRX / 32768.0); - IN_JoystickAxisEvent(joy->devid, 1, xistate.Gamepad.sThumbRY / 32768.0); - IN_JoystickAxisEvent(joy->devid, 2, xistate.Gamepad.bRightTrigger/255.0); - IN_JoystickAxisEvent(joy->devid, 3, xistate.Gamepad.sThumbLX / 32768.0); - IN_JoystickAxisEvent(joy->devid, 4, xistate.Gamepad.sThumbLY / 32768.0); - IN_JoystickAxisEvent(joy->devid, 5, xistate.Gamepad.bLeftTrigger/255.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_RIGHT, xistate.Gamepad.sThumbLX / 32768.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_DOWN, xistate.Gamepad.sThumbLY / 32768.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_TRIGGER, xistate.Gamepad.bLeftTrigger/255.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_RIGHT, xistate.Gamepad.sThumbRX / 32768.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_DOWN, xistate.Gamepad.sThumbRY / 32768.0); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_TRIGGER, xistate.Gamepad.bRightTrigger/255.0); vibrator.wLeftMotorSpeed = xinput_leftvibrator.value * 0xffff; vibrator.wRightMotorSpeed = xinput_rightvibrator.value * 0xffff; @@ -2036,12 +2052,12 @@ qboolean INS_ReadJoystick (struct wjoy_s *joy) joy->buttonstate = ji.dwButtons; if (joy->devid != DEVID_UNSET) { - IN_JoystickAxisEvent(joy->devid, 0, (ji.dwXpos - 32768.0) / 32768); - IN_JoystickAxisEvent(joy->devid, 1, (ji.dwYpos - 32768.0) / 32768); - IN_JoystickAxisEvent(joy->devid, 2, (ji.dwZpos - 32768.0) / 32768); - IN_JoystickAxisEvent(joy->devid, 3, (ji.dwRpos - 32768.0) / 32768); - IN_JoystickAxisEvent(joy->devid, 4, (ji.dwUpos - 32768.0) / 32768); - IN_JoystickAxisEvent(joy->devid, 5, (ji.dwVpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_RIGHT, (ji.dwXpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_DOWN, (ji.dwYpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_LT_AUX, (ji.dwZpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_RIGHT, (ji.dwRpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_DOWN, (ji.dwUpos - 32768.0) / 32768); + IN_JoystickAxisEvent(joy->devid, GPAXIS_RT_AUX, (ji.dwVpos - 32768.0) / 32768); } return true; } diff --git a/engine/client/keys.c b/engine/client/keys.c index 21a2d118..51cd46d9 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -260,6 +260,25 @@ keyname_t keynames[] = {"BACKQUOTE", '`'}, {"BACKSLASH", '\\'}, + {"GP_A", K_GP_A}, + {"GP_B", K_GP_B}, + {"GP_X", K_GP_X}, + {"GP_Y", K_GP_Y}, + {"GP_LSHOULDER", K_GP_LEFT_SHOULDER}, + {"GP_RSHOULDER", K_GP_RIGHT_SHOULDER}, + {"GP_LTRIGGER", K_GP_LEFT_TRIGGER}, + {"GP_RTRIGGER", K_GP_RIGHT_TRIGGER}, + {"GP_BACK", K_GP_BACK}, + {"GP_START", K_GP_START}, + {"GP_LTHUMB", K_GP_LEFT_THUMB}, + {"GP_RTHUMB", K_GP_RIGHT_THUMB}, + {"GP_DPAD_UP", K_GP_DPAD_UP}, + {"GP_DPAD_DOWN", K_GP_DPAD_DOWN}, + {"GP_DPAD_LEFT", K_GP_DPAD_LEFT}, + {"GP_DPAD_RIGHT", K_GP_DPAD_RIGHT}, + {"GP_GUIDE", K_GP_GUIDE}, + {"GP_UNKNOWN", K_GP_UNKNOWN}, + {NULL, 0} }; @@ -777,7 +796,10 @@ void Key_DefaultLinkClicked(console_t *con, char *text, char *info) else { char cmdprefix[6]; - snprintf(cmdprefix, sizeof(cmdprefix), "%i ", i+1); + if (i == 0) + *cmdprefix = 0; + else + snprintf(cmdprefix, sizeof(cmdprefix), "%i ", i+1); //hey look! its you! diff --git a/engine/client/keys.h b/engine/client/keys.h index bee76198..cb90104c 100644 --- a/engine/client/keys.h +++ b/engine/client/keys.h @@ -21,6 +21,43 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef __CLIENT_KEYS_H__ #define __CLIENT_KEYS_H__ +enum +{ //fte's assumed gamepad axis + GPAXIS_LT_RIGHT = 0, + GPAXIS_LT_DOWN = 1, + GPAXIS_LT_AUX = 2, + + GPAXIS_RT_RIGHT = 3, + GPAXIS_RT_DOWN = 4, + GPAXIS_RT_AUX = 5, + +//gah +#define GPAXIS_LT_TRIGGER GPAXIS_LT_AUX +#define GPAXIS_RT_TRIGGER GPAXIS_RT_AUX +}; + +#if 1 +//gamepad alises, because I'm too lazy to define actual keys that are distinct from joysticks +#define K_GP_A K_JOY2 +#define K_GP_B K_JOY4 +#define K_GP_X K_JOY1 +#define K_GP_Y K_JOY3 +#define K_GP_LEFT_SHOULDER K_AUX1 +#define K_GP_RIGHT_SHOULDER K_AUX2 +#define K_GP_LEFT_TRIGGER K_AUX9 +#define K_GP_RIGHT_TRIGGER K_AUX10 +#define K_GP_BACK K_AUX6 +#define K_GP_START K_AUX5 +#define K_GP_LEFT_THUMB K_AUX3 +#define K_GP_RIGHT_THUMB K_AUX4 +#define K_GP_DPAD_UP K_UPARROW +#define K_GP_DPAD_DOWN K_DOWNARROW +#define K_GP_DPAD_LEFT K_LEFTARROW +#define K_GP_DPAD_RIGHT K_RIGHTARROW +#define K_GP_GUIDE K_AUX7 +#define K_GP_UNKNOWN K_AUX8 +#endif + // // these are the key numbers that should be passed to Key_Event // @@ -108,6 +145,22 @@ K_MOUSE10, K_MWHEELUP, K_MWHEELDOWN, // 189 +#ifndef K_GP_A +K_GP_A = 190, +K_GP_B = 191, +K_GP_X = 192, +K_GP_Y = 193, +K_GP_LEFT_SHOULDER = 194, +K_GP_RIGHT_SHOULDER = 195, +K_GP_LEFT_TRIGGER = 196, +K_GP_RIGHT_TRIGGER = 197, +K_GP_BACK = 198, +K_GP_START = 199, +K_GP_LEFT_THUMB = 200, +K_GP_RIGHT_THUMB = 201, +K_GP_GUIDE = 202, +#endif + // // joystick buttons // @@ -163,6 +216,17 @@ K_RCTRL = 246, K_RSHIFT = 247, K_PRINTSCREEN = 248, +//K_UNUSED = 249, +//K_UNUSED = 250, + +#ifndef K_GP_DPAD_UP +K_GP_DPAD_UP = 251, +K_GP_DPAD_DOWN = 252, +K_GP_DPAD_LEFT = 253, +K_GP_DPAD_RIGHT = 254, +K_GP_UNKNOWN = 255, +#endif + K_MAX = 256 }; @@ -172,6 +236,7 @@ K_MAX = 256 #define KEY_MODIFIER_ALTBINDMAP (1<<3) #define KEY_MODIFIERSTATES (1<<4) +//legacy aliases, lest we ever forget! #define K_SHIFT K_LSHIFT #define K_CTRL K_LCTRL #define K_ALT K_LALT diff --git a/engine/client/net_master.c b/engine/client/net_master.c index c54cf0d2..7b411ea4 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -64,6 +64,9 @@ extern cvar_t sv_listen_qw; extern cvar_t sv_listen_nq; extern cvar_t sv_listen_dp; extern cvar_t sv_listen_q3; +#ifdef HAVE_DTLS +extern cvar_t sv_listen_dtls; +#endif typedef struct { enum masterprotocol_e protocol; @@ -3310,7 +3313,7 @@ void CL_MasterListParse(netadrtype_t adrtype, int type, qboolean slashpad) return; } - MSG_ReadByte (); + MSG_ReadByte (); //should be \n last = firstserver; diff --git a/engine/client/pr_clcmd.c b/engine/client/pr_clcmd.c index 0d3e4b96..6a6d1195 100644 --- a/engine/client/pr_clcmd.c +++ b/engine/client/pr_clcmd.c @@ -140,6 +140,25 @@ int MP_TranslateFTEtoQCCodes(int code) case K_AUX31: return 814; case K_AUX32: return 815; +#ifndef K_GP_DPAD_UP // these are probably just aliases, so dupe cases. left for completeness. + case K_GP_DPAD_UP: return 816; + case K_GP_DPAD_DOWN: return 817; + case K_GP_DPAD_LEFT: return 818; + case K_GP_DPAD_RIGHT: return 819; + case K_GP_START: return 820; + case K_GP_BACK: return 821; + case K_GP_LEFT_THUMB: return 822; + case K_GP_RIGHT_THUMB: return 823; + case K_GP_LEFT_SHOULDER:return 824; + case K_GP_RIGHT_SHOULDER:return 825; + case K_GP_A: return 826; + case K_GP_B: return 827; + case K_GP_X: return 828; + case K_GP_Y: return 829; + case K_GP_LEFT_TRIGGER: return 830; + case K_GP_RIGHT_TRIGGER:return 831; +#endif + case K_VOLUP: return -code; case K_VOLDOWN: return -code; case K_APP: return -code; @@ -274,6 +293,32 @@ int MP_TranslateQCtoFTECodes(int code) case 813: return K_AUX30; case 814: return K_AUX31; case 815: return K_AUX32; + + //WARNING: these are currently aliases in FTE. + case 816: return K_GP_DPAD_UP; + case 817: return K_GP_DPAD_DOWN; + case 818: return K_GP_DPAD_LEFT; + case 819: return K_GP_DPAD_RIGHT; + case 820: return K_GP_START; + case 821: return K_GP_BACK; + case 822: return K_GP_LEFT_THUMB; + case 823: return K_GP_RIGHT_THUMB; + case 824: return K_GP_LEFT_SHOULDER; + case 825: return K_GP_RIGHT_SHOULDER; + case 826: return K_GP_A; + case 827: return K_GP_B; + case 828: return K_GP_X; + case 829: return K_GP_Y; + case 830: return K_GP_LEFT_TRIGGER; + case 831: return K_GP_RIGHT_TRIGGER; +// case 832: return K_GP_LEFT_THUMB_UP; +// case 833: return K_GP_LEFT_THUMB_DOWN; +// case 834: return K_GP_LEFT_THUMB_LEFT; +// case 835: return K_GP_LEFT_THUMB_RIGHT; +// case 836: return K_GP_RIGHT_THUMB_UP; +// case 837: return K_GP_RIGHT_THUMB_DOWN; +// case 838: return K_GP_RIGHT_THUMB_LEFT; +// case 839: return K_GP_RIGHT_THUMB_RIGHT; default: if (code < 0) //negative values are 'fte-native' keys, for stuff that the api lacks. return -code; diff --git a/engine/client/sys_sdl.c b/engine/client/sys_sdl.c index 19c04d0d..bc91a3da 100644 --- a/engine/client/sys_sdl.c +++ b/engine/client/sys_sdl.c @@ -135,13 +135,12 @@ qboolean Sys_rmdir (const char *path) #if WIN32 ret = _rmdir (path); #else - //user, group, others - ret = rmdir (path, 0755); //WARNING: DO NOT RUN AS ROOT! + ret = rmdir (path); #endif if (ret == 0) return true; - if (errno == ENOENT) - return true; +// if (errno == ENOENT) +// return true; return false; } diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index f0e33286..65fa4e67 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -293,6 +293,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define RTLIGHTS //realtime lighting #endif +// #define QWOVERQ3 //allows qw servers with q3 clients. requires specific cgame. + #define VM_Q1 //q1 qvm gamecode interface //#define VM_LUA //q1 lua gamecode interface @@ -531,6 +533,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #if defined(HAVE_WINSSPI) || defined(HAVE_GNUTLS) #define HAVE_SSL #endif +#if defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI) + //FIXME: HAVE_WINSSPI does not work as a server. + //FIXME: advertising dtls without a valid certificate will probably bug out if a client tries to auto-upgrade. +// #define HAVE_DTLS +#endif #if defined(USE_SQLITE) || defined(USE_MYSQL) #define SQL @@ -545,6 +552,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define Q3BSPS //rbsp might as well depend upon q3bsp - its the same thing but with more lightstyles (support for which can bog down the renderer a little). #endif +#if defined(QWOVERQ3) && !defined(Q3SERVER) + #undef QWOVERQ3 +#endif + //fix things a little... #ifdef NPQTV #define NPFTE diff --git a/engine/common/net.h b/engine/common/net.h index 9baf13db..2e3865d7 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -48,7 +48,6 @@ typedef enum { NP_TLS, NP_WS, NP_WSS, - NP_IRC, NP_NATPMP } netproto_t; @@ -114,6 +113,7 @@ void NET_Init (void); void NET_Tick (void); void SVNET_RegisterCvars(void); void NET_InitClient (qboolean loopbackonly); +void NET_CloseClient(void); void NET_InitServer (void); qboolean NET_WasSpecialPacket(netsrc_t netsrc); void NET_CloseServer (void); @@ -161,6 +161,13 @@ qboolean NET_CompareAdrMasked(netadr_t *a, netadr_t *b, netadr_t *mask); qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, netproto_t addrprot, qboolean islisten); +#ifdef HAVE_DTLS +qboolean NET_DTLS_Create(struct ftenet_connections_s *col, netadr_t *to); +qboolean NET_DTLS_Decode(struct ftenet_connections_s *col); +qboolean NET_DTLS_Disconnect(struct ftenet_connections_s *col, netadr_t *to); +void NET_DTLS_Timeouts(struct ftenet_connections_s *col); +#endif + //============================================================================ #define OLD_AVG 0.99 // total = oldtotal*OLD_AVG + new*(1-OLD_AVG) diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 1b6d96fa..08ab6d5b 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -575,16 +575,6 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) send.maxsize = MAX_NQMSGLEN + PACKET_HEADER; send.cursize = 0; - if (NET_AddrIsReliable(&chan->remote_address) && chan->reliable_length) - { - //if over tcp, everything is assumed to be reliable. pretend it got acked. - chan->reliable_length = 0; //they got the entire message - chan->reliable_start = 0; - chan->incoming_reliable_acknowledged = chan->reliable_sequence; - chan->reliable_sequence++; - chan->nqreliable_allowed = true; - } - /*unreliables flood out, but reliables are tied to server sequences*/ if (chan->nqreliable_resendtime < realtime) chan->nqreliable_allowed = true; @@ -606,6 +596,9 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) { MSG_WriteLong(&send, 0); MSG_WriteLong(&send, LongSwap(chan->reliable_sequence)); + + //limit the payload length to nq's datagram max size. + //relax the limitation if its reliable (ie: over tcp) where its assumed to have no real limit if (i > MAX_NQDATAGRAM && !NET_AddrIsReliable(&chan->remote_address)) i = MAX_NQDATAGRAM; @@ -623,20 +616,31 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate) } else *(int*)send_buf = BigLong(NETFLAG_DATA | send.cursize); - NET_SendPacket (chan->sock, send.cursize, send.data, &chan->remote_address); + chan->bytesout += send.cursize; - sentsize += send.cursize; - if (showpackets.value) Con_Printf ("out %s r s=%i %i\n" , chan->sock == NS_SERVER?"s2c":"c2s" , chan->reliable_sequence , send.cursize); - send.cursize = 0; - chan->nqreliable_allowed = false; chan->nqreliable_resendtime = realtime + 0.3; //resend reliables after 0.3 seconds. nq transports suck. + + if (NET_SendPacket (chan->sock, send.cursize, send.data, &chan->remote_address) == NETERR_SENT && NET_AddrIsReliable(&chan->remote_address)) + { //if over tcp, everything is assumed to be reliable. pretend it got acked now. + //if we get an ack later, then who cares. + chan->reliable_start += i; + if (chan->reliable_start >= chan->reliable_length) + { + chan->reliable_length = 0; //they got the entire message + chan->reliable_start = 0; + } + chan->incoming_reliable_acknowledged = chan->reliable_sequence; + chan->reliable_sequence++; + chan->nqreliable_allowed = true; + } + send.cursize = 0; } } diff --git a/engine/common/net_ice.c b/engine/common/net_ice.c index 219b24a3..867d2b98 100644 --- a/engine/common/net_ice.c +++ b/engine/common/net_ice.c @@ -615,9 +615,8 @@ qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const char *val #ifndef CLIENTONLY else if (con->proto == ICEP_QWSERVER) { - extern void SVC_GetChallenge(qboolean nodpresponse); net_from = con->chosenpeer; - SVC_GetChallenge(true); + SVC_GetChallenge(false); } #endif if (con->state == ICE_CONNECTED) @@ -873,12 +872,13 @@ qboolean QDECL ICE_Get(struct icestate_s *con, const char *prop, char *value, si if (con->proto == ICEP_QWSERVER || con->proto == ICEP_QWCLIENT) { +#ifdef HAVE_DTLS Q_strncatz(value, "m=application 9 DTLS/SCTP 5000\n", valuelen); +#endif } for (i = 0; i < countof(con->codec); i++) { - int codec = atoi(prop+5); if (!con->codec[i]) continue; @@ -917,7 +917,6 @@ qboolean QDECL ICE_GetLCandidateSDP(struct icestate_s *con, char *out, size_t ou { if (can->dirty) { - struct icecandinfo_s *c = &can->info; can->dirty = false; ICE_CandidateToSDP(can, out, outsize); diff --git a/engine/common/net_ssl_gnutls.c b/engine/common/net_ssl_gnutls.c index b243dd88..b19bfd2c 100644 --- a/engine/common/net_ssl_gnutls.c +++ b/engine/common/net_ssl_gnutls.c @@ -4,12 +4,14 @@ #include "quakedef.h" -#define GNUTLS_DYNAMIC //statically linking is bad, because that just dynamically links to a .so that probably won't exist. - //on the other hand, it does validate that the function types are correct. +#ifndef GNUTLS_STATIC + #define GNUTLS_DYNAMIC //statically linking is bad, because that just dynamically links to a .so that probably won't exist. + //on the other hand, it does validate that the function types are correct. +#endif #ifdef HAVE_GNUTLS -#if defined(_WIN32) && !defined(MINGW) +#if defined(_WIN32) && !defined(MINGW) && 0 #define GNUTLS_VERSION "2.12.23" #define GNUTLS_SOPREFIX "" @@ -92,6 +94,7 @@ typedef int (VARGS gnutls_certificate_verify_function)(gnutls_session_t session) #else #include +#include #define gnutls_connection_end_t unsigned int #if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 3) @@ -152,6 +155,7 @@ static int (VARGS *qgnutls_certificate_set_x509_system_trust)(gnutls_certificate #else static int (VARGS *qgnutls_certificate_set_x509_trust_file)(gnutls_certificate_credentials_t cred, const char * cafile, gnutls_x509_crt_fmt_t type); #endif +static int (VARGS *qgnutls_certificate_set_x509_key_file)(gnutls_certificate_credentials_t res, const char * certfile, const char * keyfile, gnutls_x509_crt_fmt_t type); #ifdef GNUTLS_HAVE_VERIFY3 static int (VARGS *qgnutls_certificate_verify_peers3)(gnutls_session_t session, const char* hostname, unsigned int * status); static int (VARGS *qgnutls_certificate_verification_status_print)(unsigned int status, gnutls_certificate_type_t type, gnutls_datum_t * out, unsigned int flags); @@ -166,6 +170,15 @@ static gnutls_certificate_type_t (VARGS *qgnutls_certificate_type_get)(gnutls_se static void (VARGS *qgnutls_free)(void * ptr); static int (VARGS *qgnutls_server_name_set)(gnutls_session_t session, gnutls_server_name_type_t type, const void * name, size_t name_length); +#ifdef HAVE_DTLS +static int (VARGS *qgnutls_key_generate)(gnutls_datum_t * key, unsigned int key_size); +static void (VARGS *qgnutls_transport_set_pull_timeout_function)(gnutls_session_t session, gnutls_pull_timeout_func func); +static int (VARGS *qgnutls_dtls_cookie_verify)(gnutls_datum_t * key, void *client_data, size_t client_data_size, void *_msg, size_t msg_size, gnutls_dtls_prestate_st * prestate); +static int (VARGS *qgnutls_dtls_cookie_send)(gnutls_datum_t * key, void *client_data, size_t client_data_size, gnutls_dtls_prestate_st * prestate, gnutls_transport_ptr_t ptr, gnutls_push_func push_func); +static void (VARGS *qgnutls_dtls_prestate_set)(gnutls_session_t session, gnutls_dtls_prestate_st * prestate); +static void (VARGS *qgnutls_dtls_set_mtu)(gnutls_session_t session, unsigned int mtu); +#endif + static qboolean Init_GNUTLS(void) { #ifdef GNUTLS_HAVE_SYSTEMTRUST @@ -186,6 +199,18 @@ static qboolean Init_GNUTLS(void) GNUTLS_FUNC(gnutls_certificate_get_peers) #endif +#ifdef HAVE_DTLS +#define GNUTLS_DTLS_STUFF \ + GNUTLS_FUNC(gnutls_key_generate) \ + GNUTLS_FUNC(gnutls_transport_set_pull_timeout_function) \ + GNUTLS_FUNC(gnutls_dtls_cookie_verify) \ + GNUTLS_FUNC(gnutls_dtls_cookie_send) \ + GNUTLS_FUNC(gnutls_dtls_prestate_set) \ + GNUTLS_FUNC(gnutls_dtls_set_mtu) +#else + #define GNUTLS_DTLS_STUFF +#endif + #define GNUTLS_FUNCS \ GNUTLS_FUNC(gnutls_bye) \ @@ -208,10 +233,12 @@ static qboolean Init_GNUTLS(void) GNUTLS_FUNC(gnutls_session_get_ptr) \ GNUTLS_FUNC(gnutls_session_set_ptr) \ GNUTLS_TRUSTFUNCS \ + GNUTLS_FUNC(gnutls_certificate_set_x509_key_file) \ GNUTLS_VERIFYFUNCS \ GNUTLS_FUNC(gnutls_certificate_type_get) \ GNUTLS_FUNC(gnutls_free) \ GNUTLS_FUNC(gnutls_server_name_set) \ + GNUTLS_DTLS_STUFF #ifdef GNUTLS_DYNAMIC dllhandle_t *hmod; @@ -247,6 +274,7 @@ static qboolean Init_GNUTLS(void) #else {(void**)&qgnutls_certificate_set_x509_trust_file, "gnutls_certificate_set_x509_trust_file"}, #endif + {(void**)&qgnutls_certificate_set_x509_key_file, "gnutls_certificate_set_x509_key_file"}, #ifdef GNUTLS_HAVE_VERIFY3 {(void**)&qgnutls_certificate_verify_peers3, "gnutls_certificate_verify_peers3"}, {(void**)&qgnutls_certificate_verification_status_print, "gnutls_certificate_verification_status_print"}, @@ -260,6 +288,16 @@ static qboolean Init_GNUTLS(void) {(void**)&qgnutls_certificate_type_get, "gnutls_certificate_type_get"}, {(void**)&qgnutls_free, "gnutls_free"}, {(void**)&qgnutls_server_name_set, "gnutls_server_name_set"}, + +#ifdef HAVE_DTLS + {(void**)&qgnutls_key_generate, "gnutls_key_generate"}, + {(void**)&qgnutls_transport_set_pull_timeout_function, "gnutls_transport_set_pull_timeout_function"}, + {(void**)&qgnutls_dtls_cookie_verify, "gnutls_dtls_cookie_verify"}, + {(void**)&qgnutls_dtls_cookie_send, "gnutls_dtls_cookie_send"}, + {(void**)&qgnutls_dtls_prestate_set, "gnutls_dtls_prestate_set"}, + {(void**)&qgnutls_dtls_set_mtu, "gnutls_dtls_set_mtu"}, +#endif + {NULL, NULL} }; @@ -282,11 +320,6 @@ static qboolean Init_GNUTLS(void) return true; } -struct sslbuf -{ - char data[8192]; - int avail; -}; typedef struct { vfsfile_t funcs; @@ -298,10 +331,13 @@ typedef struct qboolean handshaking; qboolean datagram; - struct sslbuf outplain; - struct sslbuf outcrypt; - struct sslbuf inplain; - struct sslbuf incrypt; + qboolean challenging; //not sure this is actually needed, but hey. + void *cbctx; + neterr_t(*cbpush)(void *cbctx, const qbyte *data, size_t datasize); + qbyte *readdata; + size_t readsize; + gnutls_dtls_prestate_st prestate; +// int mtu; } gnutlsfile_t; #define CAFILE "/etc/ssl/certs/ca-certificates.crt" @@ -456,7 +492,10 @@ int SSL_DoHandshake(gnutlsfile_t *file) int err; //session was previously closed = error if (!file->session) + { + Sys_Printf("null session\n"); return -1; + } err = qgnutls_handshake (file->session); if (err < 0) @@ -466,7 +505,7 @@ int SSL_DoHandshake(gnutlsfile_t *file) return 0; //certificate errors etc -// qgnutls_perror (err); + qgnutls_perror (err); SSL_Close(&file->funcs); // Con_Printf("%s: abort\n", file->certname); @@ -560,6 +599,7 @@ static qofs_t QDECL SSL_GetLen (struct vfsfile_s *file) static ssize_t SSL_Push(gnutls_transport_ptr_t p, const void *data, size_t size) { gnutlsfile_t *file = p; +// Sys_Printf("SSL_Push: %u\n", size); int done = VFS_WRITE(file->stream, data, size); if (!done) { @@ -571,32 +611,10 @@ static ssize_t SSL_Push(gnutls_transport_ptr_t p, const void *data, size_t size) return 0; return done; } -/*static ssize_t SSL_PushV(gnutls_transport_ptr_t p, giovec_t *iov, int iovcnt) -{ - int i; - ssize_t written; - ssize_t total; - gnutlsfile_t *file = p; - for (i = 0; i < iovcnt; i++) - { - written = SSL_Push(file, iov[i].iov_base, iov[i].iov_len); - if (written <= 0) - break; - total += written; - if (written < iov[i].iov_len) - break; - } - if (!total) - { - qgnutls_transport_set_errno(file->session, EAGAIN); - return -1; - } - qgnutls_transport_set_errno(file->session, 0); - return total; -}*/ static ssize_t SSL_Pull(gnutls_transport_ptr_t p, void *data, size_t size) { gnutlsfile_t *file = p; +// Sys_Printf("SSL_Pull: %u\n", size); int done = VFS_READ(file->stream, data, size); if (!done) { @@ -611,62 +629,174 @@ static ssize_t SSL_Pull(gnutls_transport_ptr_t p, void *data, size_t size) return done; } -vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server, qboolean datagram) +static ssize_t DTLS_Push(gnutls_transport_ptr_t p, const void *data, size_t size) +{ + gnutlsfile_t *file = p; + + neterr_t ne = file->cbpush(file->cbctx, data, size); + +// Sys_Printf("DTLS_Push: %u, err=%i\n", (unsigned)size, (int)ne); + + switch(ne) + { + case NETERR_CLOGGED: + qgnutls_transport_set_errno(file->session, EAGAIN); + return -1; + case NETERR_MTU: + qgnutls_transport_set_errno(file->session, EMSGSIZE); + return -1; + case NETERR_DISCONNECTED: + qgnutls_transport_set_errno(file->session, EPERM); + return -1; + default: + qgnutls_transport_set_errno(file->session, 0); + return size; + } +} +static ssize_t DTLS_Pull(gnutls_transport_ptr_t p, void *data, size_t size) +{ + gnutlsfile_t *file = p; + +// Sys_Printf("DTLS_Pull: %u of %u\n", size, file->readsize); + + if (!file->readsize) + { //no data left +// Sys_Printf("DTLS_Pull: EAGAIN\n"); + qgnutls_transport_set_errno(file->session, EAGAIN); + return -1; + } + else if (file->readsize > size) + { //buffer passed is smaller than available data +// Sys_Printf("DTLS_Pull: EMSGSIZE\n"); + memcpy(data, file->readdata, size); + file->readsize = 0; + qgnutls_transport_set_errno(file->session, EMSGSIZE); + return -1; + } + else + { //buffer is big enough to read it all + size = file->readsize; + file->readsize = 0; +// Sys_Printf("DTLS_Pull: reading %i\n", size); + memcpy(data, file->readdata, size); + qgnutls_transport_set_errno(file->session, 0); + return size; + } +} +static int DTLS_Pull_Timeout(gnutls_transport_ptr_t p, unsigned int timeout) +{ //gnutls (pointlessly) requires this function for dtls. + gnutlsfile_t *f = p; +// Sys_Printf("DTLS_Pull_Timeout %i, %i\n", timeout, f->readsize); + return f->readsize>0?1:0; +} + +#ifdef USE_ANON +static gnutls_anon_client_credentials_t anoncred[2]; +#else +static gnutls_certificate_credentials_t xcred[2]; +#endif +static gnutls_datum_t cookie_key; + +qboolean SSL_InitGlobal(qboolean isserver) +{ + static int initstatus[2]; + if (!initstatus[isserver]) + { + if (!Init_GNUTLS()) + { + Con_Printf("GnuTLS "GNUTLS_VERSION" library not available.\n"); + return false; + } + initstatus[isserver] = true; + qgnutls_global_init (); + + if (isserver) + qgnutls_key_generate(&cookie_key, GNUTLS_COOKIE_KEY_SIZE); + + +#ifdef USE_ANON + qgnutls_anon_allocate_client_credentials (&anoncred[isserver]); +#else + + qgnutls_certificate_allocate_credentials (&xcred[isserver]); + +#ifdef GNUTLS_HAVE_SYSTEMTRUST + qgnutls_certificate_set_x509_system_trust (xcred[isserver]); +#else + qgnutls_certificate_set_x509_trust_file (xcred[isserver], CAFILE, GNUTLS_X509_FMT_PEM); +#endif + + if (isserver) + { +#define KEYFILE "c:/games/tools/ssl/key.pem" +#define CERTFILE "c:/games/tools/ssl/cert.pem" + int ret = qgnutls_certificate_set_x509_key_file(xcred[isserver], CERTFILE, KEYFILE, GNUTLS_X509_FMT_PEM); + if (ret < 0) + { + Con_Printf("No certificate or key were found\n"); + initstatus[isserver] = -1; + } + } + else + qgnutls_certificate_set_verify_function (xcred[isserver], SSL_CheckCert); +#endif + } + + if (initstatus[isserver] < 0) + return false; + return true; +} +qboolean SSL_InitConnection(gnutlsfile_t *newf, qboolean isserver, qboolean datagram) +{ + // Initialize TLS session + qgnutls_init (&newf->session, GNUTLS_NONBLOCK|(isserver?GNUTLS_SERVER:GNUTLS_CLIENT)|(datagram?GNUTLS_DATAGRAM:0)); + + if (!isserver) + qgnutls_server_name_set(newf->session, GNUTLS_NAME_DNS, newf->certname, strlen(newf->certname)); + + qgnutls_session_set_ptr(newf->session, newf); + +#ifdef USE_ANON + //qgnutls_kx_set_priority (newf->session, kx_prio); + qgnutls_credentials_set (newf->session, GNUTLS_CRD_ANON, anoncred[isserver]); +#else +//#if GNUTLS_VERSION_MAJOR >= 3 + //gnutls_priority_set_direct(); +//#else + //qgnutls_certificate_type_set_priority (newf->session, cert_type_priority); +//#endif + qgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, xcred[isserver]); +#endif + // Use default priorities + qgnutls_set_default_priority (newf->session); + + // tell gnutls how to send/receive data + qgnutls_transport_set_ptr (newf->session, newf); + qgnutls_transport_set_push_function(newf->session, datagram?DTLS_Push:SSL_Push); + //qgnutls_transport_set_vec_push_function(newf->session, SSL_PushV); + qgnutls_transport_set_pull_function(newf->session, datagram?DTLS_Pull:SSL_Pull); + if (datagram) + qgnutls_transport_set_pull_timeout_function(newf->session, DTLS_Pull_Timeout); + +// if (isserver) //don't bother to auth any client certs +// qgnutls_certificate_server_set_request(newf->session, GNUTLS_CERT_IGNORE); + + newf->handshaking = true; + + return true; +} + +vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean isserver) { gnutlsfile_t *newf; - qboolean anon = false; - - static gnutls_anon_client_credentials_t anoncred; - static gnutls_certificate_credentials_t xcred; - -// long _false = false; -// long _true = true; - - /* Need to enable anonymous KX specifically. */ -// const int kx_prio[] = {GNUTLS_KX_ANON_DH, 0}; -// const int cert_type_priority[3] = {GNUTLS_CRT_X509, 0}; if (!source) return NULL; - if (datagram) - { - VFS_CLOSE(source); - return NULL; - } - -#ifdef GNUTLS_DATAGRAM - if (datagram) - return NULL; -#endif - - { - static qboolean needinit = true; - if (needinit) - { - if (!Init_GNUTLS()) - { - Con_Printf("GnuTLS "GNUTLS_VERSION" library not available.\n"); - VFS_CLOSE(source); - return NULL; - } - qgnutls_global_init (); - - qgnutls_anon_allocate_client_credentials (&anoncred); - qgnutls_certificate_allocate_credentials (&xcred); - -#ifdef GNUTLS_HAVE_SYSTEMTRUST - qgnutls_certificate_set_x509_system_trust (xcred); -#else - qgnutls_certificate_set_x509_trust_file (xcred, CAFILE, GNUTLS_X509_FMT_PEM); -#endif - qgnutls_certificate_set_verify_function (xcred, SSL_CheckCert); - - needinit = false; - } - } - - newf = Z_Malloc(sizeof(*newf)); + if (!SSL_InitGlobal(isserver)) + newf = NULL; + else + newf = Z_Malloc(sizeof(*newf)); if (!newf) { VFS_CLOSE(source); @@ -682,51 +812,189 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server, newf->funcs.Tell = SSL_Tell; newf->funcs.seekstyle = SS_UNSEEKABLE; - Q_strncpyz(newf->certname, hostname, sizeof(newf->certname)); - - // Initialize TLS session - qgnutls_init (&newf->session, GNUTLS_CLIENT/*|(datagram?GNUTLS_DATAGRAM:0)*/); - - qgnutls_server_name_set(newf->session, GNUTLS_NAME_DNS, newf->certname, strlen(newf->certname)); - - qgnutls_session_set_ptr(newf->session, newf); - - // Use default priorities - qgnutls_set_default_priority (newf->session); - if (anon) - { - //qgnutls_kx_set_priority (newf->session, kx_prio); - qgnutls_credentials_set (newf->session, GNUTLS_CRD_ANON, anoncred); - } + if (hostname) + Q_strncpyz(newf->certname, hostname, sizeof(newf->certname)); else + Q_strncpyz(newf->certname, "", sizeof(newf->certname)); + + if (!SSL_InitConnection(newf, isserver, false)) { -//#if GNUTLS_VERSION_MAJOR >= 3 - //gnutls_priority_set_direct(); -//#else - //qgnutls_certificate_type_set_priority (newf->session, cert_type_priority); -//#endif - qgnutls_credentials_set (newf->session, GNUTLS_CRD_CERTIFICATE, xcred); + VFS_CLOSE(&newf->funcs); + return NULL; } - // tell gnutls how to send/receive data - qgnutls_transport_set_ptr (newf->session, newf); - qgnutls_transport_set_push_function(newf->session, SSL_Push); - //qgnutls_transport_set_vec_push_function(newf->session, SSL_PushV); - qgnutls_transport_set_pull_function(newf->session, SSL_Pull); - //qgnutls_transport_set_pull_timeout_function(newf->session, NULL); - - newf->handshaking = true; - return &newf->funcs; } -/* -vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server, qboolean datagram) + + +#ifdef HAVE_DTLS + +void DTLS_DestroyContext(void *ctx) { - if (source) - VFS_CLOSE(source); - return NULL; + SSL_Close(ctx); +} +qboolean DTLS_HasServerCertificate(void) +{ + if (!SSL_InitGlobal(true)) + return false; + return true; +} +void *DTLS_CreateContext(void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver) +{ + gnutlsfile_t *newf; + + if (!SSL_InitGlobal(isserver)) + newf = NULL; + else + newf = Z_Malloc(sizeof(*newf)); + if (!newf) + return NULL; + newf->datagram = true; + newf->cbctx = cbctx; + newf->cbpush = push; + newf->challenging = isserver; + +// Sys_Printf("DTLS_CreateContext: server=%i\n", isserver); + + Q_strncpyz(newf->certname, "", sizeof(newf->certname)); + + if (!SSL_InitConnection(newf, isserver, true)) + { + SSL_Close(&newf->funcs); + return NULL; + } + + return newf; +} + +neterr_t DTLS_Transmit(void *ctx, const qbyte *data, size_t datasize) +{ + int ret; + gnutlsfile_t *f = (gnutlsfile_t *)ctx; +// Sys_Printf("DTLS_Transmit: %u\n", datasize); +// Sys_Printf("%su\n", data); + + if (f->challenging) + return NETERR_CLOGGED; + if (f->handshaking) + { + ret = SSL_DoHandshake(f); + if (!ret) + return NETERR_CLOGGED; + if (ret < 0) + return NETERR_DISCONNECTED; + } + + ret = qgnutls_record_send(f->session, data, datasize); + if (ret < 0) + { + if (ret == GNUTLS_E_LARGE_PACKET) + return NETERR_MTU; +//Sys_Error("qgnutls_record_send returned %i\n", ret); + + if (qgnutls_error_is_fatal(ret)) + return NETERR_DISCONNECTED; + return NETERR_CLOGGED; + } + return NETERR_SENT; +} + +neterr_t DTLS_Received(void *ctx, qbyte *data, size_t datasize) +{ + int cli_addr = 0xdeadbeef; + int ret; + gnutlsfile_t *f = (gnutlsfile_t *)ctx; + +//Sys_Printf("DTLS_Received: %u\n", datasize); + + if (f->challenging) + { + memset(&f->prestate, 0, sizeof(f->prestate)); + ret = qgnutls_dtls_cookie_verify(&cookie_key, + &cli_addr, sizeof(cli_addr), + data, datasize, + &f->prestate); + + if (ret < 0) + { +//Sys_Printf("Sending cookie\n"); + qgnutls_dtls_cookie_send(&cookie_key, + &cli_addr, sizeof(cli_addr), + &f->prestate, + (gnutls_transport_ptr_t)f, DTLS_Push); + return NETERR_CLOGGED; + } +//Sys_Printf("Got correct cookie\n"); + f->challenging = false; + + qgnutls_dtls_prestate_set(f->session, &f->prestate); + qgnutls_dtls_set_mtu(f->session, 1440); + +// qgnutls_transport_set_push_function(f->session, DTLS_Push); +// qgnutls_transport_set_pull_function(f->session, DTLS_Pull); + f->handshaking = true; + } + + f->readdata = data; + f->readsize = datasize; + + if (f->handshaking) + { + ret = SSL_DoHandshake(f); + if (ret <= 0) + f->readsize = 0; + if (!ret) + return NETERR_CLOGGED; + if (ret < 0) + return NETERR_DISCONNECTED; + } + + ret = qgnutls_record_recv(f->session, net_message_buffer, sizeof(net_message_buffer)); +//Sys_Printf("DTLS_Received returned %i of %i\n", ret, f->readsize); + f->readsize = 0; + if (ret <= 0) + { + if (!ret) + { +// Sys_Printf("DTLS_Received peer terminated connection\n"); + return NETERR_DISCONNECTED; + } + if (qgnutls_error_is_fatal(ret)) + { +// Sys_Printf("DTLS_Received fail error\n"); + return NETERR_DISCONNECTED; + } +// Sys_Printf("DTLS_Received temp error\n"); + return NETERR_CLOGGED; + } + net_message.cursize = ret; + data[ret] = 0; +// Sys_Printf("DTLS_Received returned %s\n", data); + return NETERR_SENT; +} + +neterr_t DTLS_Timeouts(void *ctx) +{ + gnutlsfile_t *f = (gnutlsfile_t *)ctx; + int ret; + if (f->challenging) + return NETERR_CLOGGED; + if (f->handshaking) + { + f->readsize = 0; + ret = SSL_DoHandshake(f); + f->readsize = 0; + if (!ret) + return NETERR_CLOGGED; + if (ret < 0) + return NETERR_DISCONNECTED; + +// Sys_Printf("handshaking over?\n"); + } + return NETERR_SENT; } -*/ +#endif + #endif diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index 0a0ac70b..d4353422 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -1,5 +1,12 @@ #include "quakedef.h" #if defined(HAVE_WINSSPI) + +/*regarding HAVE_DTLS +DTLS1.0 is supported from win8 onwards +Its also meant to be supported from some RDP server patch on win7, but I can't get it to work. +I've given up for now. +*/ + cvar_t *tls_ignorecertificateerrors; #include "winquake.h" @@ -32,6 +39,10 @@ cvar_t *tls_ignorecertificateerrors; #define SCH_CRED_SNI_CREDENTIAL 0x00080000 #endif +#define SEC_I_MESSAGE_FRAGMENT 0x00090364L +#define SEC_E_INVALID_PARAMETER 0x8009035DL + + //hungarian ensures we hit no macros. static struct { @@ -39,7 +50,7 @@ static struct SECURITY_STATUS (WINAPI *pDecryptMessage) (PCtxtHandle,PSecBufferDesc,ULONG,PULONG); SECURITY_STATUS (WINAPI *pEncryptMessage) (PCtxtHandle,ULONG,PSecBufferDesc,ULONG); SECURITY_STATUS (WINAPI *pAcquireCredentialsHandleA) (SEC_CHAR*,SEC_CHAR*,ULONG,PLUID,PVOID,SEC_GET_KEY_FN,PVOID,PCredHandle,PTimeStamp); - SECURITY_STATUS (WINAPI *pInitializeSecurityContextA) (PCredHandle,PCtxtHandle,SEC_CHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp); +// SECURITY_STATUS (WINAPI *pInitializeSecurityContextA) (PCredHandle,PCtxtHandle,SEC_CHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp); SECURITY_STATUS (WINAPI *pInitializeSecurityContextW) (PCredHandle,PCtxtHandle,SEC_WCHAR*,ULONG,ULONG,ULONG,PSecBufferDesc,ULONG,PCtxtHandle,PSecBufferDesc,PULONG,PTimeStamp); SECURITY_STATUS (WINAPI *pAcceptSecurityContext) (PCredHandle,PCtxtHandle,PSecBufferDesc,unsigned long,unsigned long,PCtxtHandle,PSecBufferDesc,unsigned long SEC_FAR *,PTimeStamp); SECURITY_STATUS (WINAPI *pCompleteAuthToken) (PCtxtHandle,PSecBufferDesc); @@ -65,7 +76,7 @@ void SSL_Init(void) {(void**)&secur.pDecryptMessage, "DecryptMessage"}, {(void**)&secur.pEncryptMessage, "EncryptMessage"}, {(void**)&secur.pAcquireCredentialsHandleA, "AcquireCredentialsHandleA"}, - {(void**)&secur.pInitializeSecurityContextA, "InitializeSecurityContextA"}, +// {(void**)&secur.pInitializeSecurityContextA, "InitializeSecurityContextA"}, {(void**)&secur.pInitializeSecurityContextW, "InitializeSecurityContextW"}, {(void**)&secur.pAcceptSecurityContext, "AcceptSecurityContext"}, {(void**)&secur.pCompleteAuthToken, "CompleteAuthToken"}, @@ -98,14 +109,13 @@ qboolean SSL_Inited(void) return !!secur.lib && !!crypt.lib; } -#define MessageAttribute (ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MANUAL_CRED_VALIDATION) +#define MessageAttribute (ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | ISC_REQ_EXTENDED_ERROR | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_MANUAL_CRED_VALIDATION) struct sslbuf { size_t datasize; char *data; size_t avail; - size_t newd; }; typedef struct { @@ -137,6 +147,11 @@ typedef struct { SecHandle sechnd; int headersize, footersize; char headerdata[1024], footerdata[1024]; + +#ifdef HAVE_DTLS + void *cbctx; + void (*transmit)(void *cbctx, qbyte *data, size_t datasize); +#endif } sslfile_t; static int SSPI_ExpandBuffer(struct sslbuf *buf, size_t bytes) @@ -162,8 +177,15 @@ static int SSPI_CopyIntoBuffer(struct sslbuf *buf, const void *data, unsigned in static void SSPI_Error(sslfile_t *f, char *error, ...) { + va_list argptr; + char string[1024]; + va_start (argptr, error); + vsnprintf (string,sizeof(string)-1, error,argptr); + va_end (argptr); + f->handshaking = HS_ERROR; - Sys_Printf("%s", error); + if (*string) + Sys_Printf("%s", string); if (f->stream) VFS_CLOSE(f->stream); @@ -176,7 +198,17 @@ static void SSPI_TryFlushCryptOut(sslfile_t *f) { int sent; if (f->outcrypt.avail) + { +#ifdef HAVE_DTLS + if (f->transmit) + { + f->transmit(f->cbctx, f->outcrypt.data, f->outcrypt.avail); + f->outcrypt.avail = 0; + return; + } +#endif sent = VFS_WRITE(f->stream, f->outcrypt.data, f->outcrypt.avail); + } else return; @@ -215,7 +247,7 @@ static void SSPI_Decode(sslfile_t *f) return; BuffDesc.ulVersion = SECBUFFER_VERSION; - BuffDesc.cBuffers = 4; + BuffDesc.cBuffers = countof(SecBuff); BuffDesc.pBuffers = SecBuff; SecBuff[0].BufferType = SECBUFFER_DATA; @@ -238,6 +270,7 @@ static void SSPI_Decode(sslfile_t *f) } switch(ss) { + case SEC_E_DECRYPT_FAILURE: SSPI_Error(f, "DecryptMessage failed: SEC_E_DECRYPT_FAILURE\n", ss); break; case SEC_E_INVALID_HANDLE: SSPI_Error(f, "DecryptMessage failed: SEC_E_INVALID_HANDLE\n"); break; default: SSPI_Error(f, "DecryptMessage failed: %0#lx\n", ss); break; } @@ -318,6 +351,8 @@ static void SSPI_Encode(sslfile_t *f) SecBuff[2].pvBuffer = f->footerdata; SecBuff[3].BufferType = SECBUFFER_EMPTY; + SecBuff[3].cbBuffer = 0; + SecBuff[3].pvBuffer = NULL; ss = secur.pEncryptMessage(&f->sechnd, ulQop, &BuffDesc, 0); @@ -367,9 +402,19 @@ static struct }; char *narrowen(char *out, size_t outlen, wchar_t *wide); -static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, size_t datasize) +static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, size_t datasize, qboolean datagram) { int i; + if (datagram) + { + Con_Printf("FIXME: Ring of trust not yet implemented\n"); + if (status == CERT_E_UNTRUSTEDROOT) + { + Con_Printf("Allowing (probably) self-signed cert.\n"); + status = SEC_E_OK; + } + return status; + } for (i = 0; knowncerts[i].hostname; i++) { if (!wcscmp(domain, knowncerts[i].hostname)) @@ -409,7 +454,7 @@ static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data, return status; } -static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServerName, DWORD dwCertFlags) +static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServerName, DWORD dwCertFlags, qboolean datagram) { HTTPSPolicyCallbackData polHttps; CERT_CHAIN_POLICY_PARA PolicyPara; @@ -465,7 +510,7 @@ static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServe } else { - Status = VerifyKnownCertificates(PolicyStatus.dwError, pwszServerName, pServerCert->pbCertEncoded, pServerCert->cbCertEncoded); + Status = VerifyKnownCertificates(PolicyStatus.dwError, pwszServerName, pServerCert->pbCertEncoded, pServerCert->cbCertEncoded, datagram); if (Status) { char fmsg[512]; @@ -598,14 +643,18 @@ static void SSPI_Handshake (sslfile_t *f) SECURITY_STATUS ss; TimeStamp Lifetime; SecBufferDesc OutBuffDesc; - SecBuffer OutSecBuff[2]; + SecBuffer OutSecBuff[8]; SecBufferDesc InBuffDesc; - SecBuffer InSecBuff[3]; + SecBuffer InSecBuff[8]; ULONG ContextAttributes; SCHANNEL_CRED SchannelCred; + int i; + qboolean retries = 5; - char buf1[128]; - char buf2[128]; +// char buf1[128]; +// char buf2[128]; + +retry: if (f->outcrypt.avail) { @@ -618,16 +667,19 @@ static void SSPI_Handshake (sslfile_t *f) //FIXME: skip this if we've had no new data since last time OutBuffDesc.ulVersion = SECBUFFER_VERSION; - OutBuffDesc.cBuffers = 2; + OutBuffDesc.cBuffers = countof(OutSecBuff); OutBuffDesc.pBuffers = OutSecBuff; - OutSecBuff[0].cbBuffer = f->outcrypt.datasize - f->outcrypt.avail; OutSecBuff[0].BufferType = SECBUFFER_TOKEN; + OutSecBuff[0].cbBuffer = f->outcrypt.datasize - f->outcrypt.avail; OutSecBuff[0].pvBuffer = f->outcrypt.data + f->outcrypt.avail; - OutSecBuff[1].BufferType = 16;//SECBUFFER_TARGET_HOST; - OutSecBuff[1].pvBuffer = buf1; - OutSecBuff[1].cbBuffer = sizeof(buf1); + for (i = 0; i < OutBuffDesc.cBuffers; i++) + { + OutSecBuff[i].BufferType = SECBUFFER_EMPTY; + OutSecBuff[i].pvBuffer = NULL; + OutSecBuff[i].cbBuffer = 0; + } if (f->handshaking == HS_ERROR) return; //gave up. @@ -653,29 +705,52 @@ static void SSPI_Handshake (sslfile_t *f) else if (f->handshaking == HS_CLIENT) { //only if we actually have data. - if (!f->incrypt.avail) + if (!f->incrypt.avail && !f->datagram) return; InBuffDesc.ulVersion = SECBUFFER_VERSION; - InBuffDesc.cBuffers = 2; + InBuffDesc.cBuffers = 4; InBuffDesc.pBuffers = InSecBuff; - InSecBuff[0].BufferType = SECBUFFER_TOKEN; - InSecBuff[0].cbBuffer = f->incrypt.avail; - InSecBuff[0].pvBuffer = f->incrypt.data; + i = 0; - InSecBuff[1].BufferType = SECBUFFER_EMPTY; - InSecBuff[1].pvBuffer = NULL; - InSecBuff[1].cbBuffer = 0; + if (f->incrypt.avail) + { + InSecBuff[i].BufferType = SECBUFFER_TOKEN; + InSecBuff[i].cbBuffer = f->incrypt.avail; + InSecBuff[i].pvBuffer = f->incrypt.data; + i++; + } - ss = secur.pInitializeSecurityContextA (&f->cred, &f->sechnd, NULL, MessageAttribute|(f->datagram?ISC_REQ_DATAGRAM:ISC_REQ_STREAM), 0, SECURITY_NATIVE_DREP, &InBuffDesc, 0, &f->sechnd, &OutBuffDesc, &ContextAttributes, &Lifetime); + for (; i < InBuffDesc.cBuffers; i++) + { + InSecBuff[i].BufferType = SECBUFFER_EMPTY; + InSecBuff[i].pvBuffer = NULL; + InSecBuff[i].cbBuffer = 0; + } + + ss = secur.pInitializeSecurityContextW (&f->cred, &f->sechnd, NULL, MessageAttribute|(f->datagram?ISC_REQ_DATAGRAM:ISC_REQ_STREAM), 0, SECURITY_NETWORK_DREP, &InBuffDesc, 0, &f->sechnd, &OutBuffDesc, &ContextAttributes, &Lifetime); if (ss == SEC_E_INCOMPLETE_MESSAGE) { - if (f->incrypt.avail == f->incrypt.datasize) +// Con_Printf("SEC_E_INCOMPLETE_MESSAGE\n"); + if (!f->datagram && f->incrypt.avail == f->incrypt.datasize) SSPI_ExpandBuffer(&f->incrypt, f->incrypt.datasize+1024); return; } + else if (ss == SEC_E_INVALID_TOKEN) + { +// Con_Printf("SEC_E_INVALID_TOKEN\n"); + if (f->datagram) + return; //our udp protocol may have non-dtls packets mixed in. besides, we don't want to die from spoofed packets. + } +// else if (ss == SEC_I_MESSAGE_FRAGMENT) +// Con_Printf("SEC_I_MESSAGE_FRAGMENT\n"); +// else if (ss == SEC_I_CONTINUE_NEEDED) +// Con_Printf("SEC_I_CONTINUE_NEEDED\n"); +// else +// Con_Printf("InitializeSecurityContextA %x\n", ss); + //any extra data should still remain for the next time around. this might be more handshake data or payload data. if (InSecBuff[1].BufferType == SECBUFFER_EXTRA) @@ -692,29 +767,49 @@ static void SSPI_Handshake (sslfile_t *f) return; InBuffDesc.ulVersion = SECBUFFER_VERSION; - InBuffDesc.cBuffers = 3; + InBuffDesc.cBuffers = countof(InSecBuff); InBuffDesc.pBuffers = InSecBuff; + i = 0; - InSecBuff[0].BufferType = SECBUFFER_TOKEN; - InSecBuff[0].cbBuffer = f->incrypt.avail; - InSecBuff[0].pvBuffer = f->incrypt.data; - - InSecBuff[1].BufferType = SECBUFFER_EMPTY; - InSecBuff[1].pvBuffer = NULL; - InSecBuff[1].cbBuffer = 0; - - InSecBuff[2].BufferType = 16;//SECBUFFER_TARGET_HOST; - InSecBuff[2].pvBuffer = buf2; - InSecBuff[2].cbBuffer = sizeof(buf2); - - ss = secur.pAcceptSecurityContext(&f->cred, (f->handshaking==HS_SERVER)?&f->sechnd:NULL, &InBuffDesc, ASC_REQ_ALLOCATE_MEMORY|ASC_REQ_STREAM|ASC_REQ_CONFIDENTIALITY, SECURITY_NATIVE_DREP, &f->sechnd, &OutBuffDesc, &ContextAttributes, &Lifetime); - - if (ss == SEC_E_INCOMPLETE_MESSAGE) + if (f->incrypt.avail) { - if (f->incrypt.avail == f->incrypt.datasize) + InSecBuff[i].BufferType = SECBUFFER_TOKEN; + InSecBuff[i].cbBuffer = f->incrypt.avail; + InSecBuff[i].pvBuffer = f->incrypt.data; + i++; + } + + for (; i < InBuffDesc.cBuffers; i++) + { + InSecBuff[i].BufferType = SECBUFFER_EMPTY; + InSecBuff[i].pvBuffer = NULL; + InSecBuff[i].cbBuffer = 0; + } + + i = 1; + OutSecBuff[i++].BufferType = SECBUFFER_EXTRA; + OutSecBuff[i++].BufferType = 17/*SECBUFFER_ALERT*/; + +#define ServerMessageAttribute (ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT | ASC_REQ_CONFIDENTIALITY /*| ASC_REQ_EXTENDED_ERROR*/ | ASC_REQ_ALLOCATE_MEMORY) + + ss = secur.pAcceptSecurityContext(&f->cred, (f->handshaking==HS_SERVER)?&f->sechnd:NULL, &InBuffDesc, + ServerMessageAttribute|(f->datagram?ASC_REQ_DATAGRAM:ASC_REQ_STREAM), SECURITY_NETWORK_DREP, &f->sechnd, + &OutBuffDesc, &ContextAttributes, NULL); + if (ss == SEC_E_INVALID_TOKEN) + { +// Con_Printf("SEC_E_INVALID_TOKEN\n"); + if (f->datagram) + return; + } + else if (ss == SEC_E_INCOMPLETE_MESSAGE) + { +// Con_Printf("SEC_E_INCOMPLETE_MESSAGE\n"); + if (!f->datagram && f->incrypt.avail == f->incrypt.datasize) SSPI_ExpandBuffer(&f->incrypt, f->incrypt.datasize+1024); return; } +// else +// Con_Printf("InitializeSecurityContextA %x\n", ss); f->handshaking = HS_SERVER; //any extra data should still remain for the next time around. this might be more handshake data or payload data. @@ -743,7 +838,8 @@ static void SSPI_Handshake (sslfile_t *f) case SEC_E_INVALID_HANDLE: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_INVALID_HANDLE\n"); break; case SEC_E_ILLEGAL_MESSAGE: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_ILLEGAL_MESSAGE\n"); break; case SEC_E_INVALID_TOKEN: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_INVALID_TOKEN\n"); break; - default: SSPI_Error(f, "InitializeSecurityContext failed: %#lx\n", ss); break; + case SEC_E_INVALID_PARAMETER: SSPI_Error(f, "InitializeSecurityContext failed: SEC_E_INVALID_PARAMETER\n"); break; + default: SSPI_Error(f, "InitializeSecurityContext failed: %lx\n", (long)ss); break; } return; } @@ -758,15 +854,6 @@ static void SSPI_Handshake (sslfile_t *f) } } - if (SSPI_CopyIntoBuffer(&f->outcrypt, OutSecBuff[0].pvBuffer, OutSecBuff[0].cbBuffer, true) < OutSecBuff[0].cbBuffer) - { - SSPI_Error(f, "crypt overflow\n"); - return; - } - - //send early, send often. - SSPI_TryFlushCryptOut(f); - //its all okay and established if we get this far. if (ss == SEC_E_OK) { @@ -787,7 +874,7 @@ static void SSPI_Handshake (sslfile_t *f) SSPI_Error(f, "unable to read server's certificate\n"); return; } - if (VerifyServerCertificate(remotecert, f->wpeername, 0)) + if (VerifyServerCertificate(remotecert, f->wpeername, 0, f->datagram)) { f->handshaking = HS_ERROR; SSPI_Error(f, "Error validating certificante\n"); @@ -799,9 +886,33 @@ static void SSPI_Handshake (sslfile_t *f) } f->handshaking = HS_ESTABLISHED; - - SSPI_Encode(f); } + + //send early, send often. +#ifdef HAVE_DTLS + if (f->transmit) + { + for (i = 0; i < OutBuffDesc.cBuffers; i++) + if (OutSecBuff[i].BufferType == SECBUFFER_TOKEN && OutSecBuff[i].cbBuffer) + f->transmit(f->cbctx, OutSecBuff[i].pvBuffer, OutSecBuff[i].cbBuffer); + } + else +#endif + { + i = 0; + if (SSPI_CopyIntoBuffer(&f->outcrypt, OutSecBuff[i].pvBuffer, OutSecBuff[i].cbBuffer, true) < OutSecBuff[i].cbBuffer) + { + SSPI_Error(f, "crypt overflow\n"); + return; + } + SSPI_TryFlushCryptOut(f); + } + + if (f->handshaking == HS_ESTABLISHED) + SSPI_Encode(f); + else if (ss == SEC_I_MESSAGE_FRAGMENT) //looks like we can connect faster if we loop when we get this result. + if (retries --> 0) + goto retry; } static int QDECL SSPI_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread) @@ -886,7 +997,7 @@ static qboolean QDECL SSPI_Close (struct vfsfile_s *file) } #include -vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server, qboolean datagram) +vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server) { sslfile_t *newf; int i = 0; @@ -938,7 +1049,6 @@ vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server } newf->wpeername[i] = 0; - newf->datagram = datagram; newf->handshaking = server?HS_STARTSERVER:HS_STARTCLIENT; newf->stream = source; newf->funcs.Close = SSPI_Close; @@ -960,4 +1070,163 @@ vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server return &newf->funcs; } + + +#if 0 +struct nulldtls_s +{ + void *cbctx; + neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize); +}; +void *DTLS_CreateContext(void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver) +{ + struct nulldtls_s *ctx = Z_Malloc(sizeof(*ctx)); + ctx->cbctx = cbctx; + ctx->push = push; + return ctx; +} +qboolean DTLS_HasServerCertificate(void) +{ + //FIXME: at this point, schannel is still returning errors when I try acting as a server. + //so just block any attempt to use this as a server. + //clients don't need certs! + return false; +} +neterr_t DTLS_Transmit(void *vctx, const qbyte *data, size_t datasize) +{ + struct nulldtls_s *ctx = vctx; + neterr_t r; + *(int*)data ^= 0xdeadbeef; + r = ctx->push(ctx->cbctx, data, datasize); + *(int*)data ^= 0xdeadbeef; + return r; +} +neterr_t DTLS_Received(void *ctx, qbyte *data, size_t datasize) +{ + *(int*)data ^= 0xdeadbeef; + return NETERR_SENT; +} +#elif defined(HAVE_DTLS) +void *DTLS_CreateContext(char *remotehost, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver) +{ + int i = 0; + sslfile_t *ctx; + if (!SSL_Inited()) + return NULL; + + ctx = Z_Malloc(sizeof(*ctx)); + ctx->datagram = true; + ctx->handshaking = isserver?HS_STARTSERVER:HS_STARTCLIENT; + ctx->cbctx = cbctx; + ctx->transmit = push; + + while(*remotehost) + { + int err; + int c = utf8_decode(&err, remotehost, (void*)&remotehost); + if (c > WCHAR_MAX) + err = true; //no 16bit surrogates. they're evil. + else if (i == sizeof(ctx->wpeername)/sizeof(ctx->wpeername[0]) - 1) + err = true; //no space to store it + else + ctx->wpeername[i++] = c; + if (err) + { + Z_Free(ctx); + return NULL; + } + } + ctx->wpeername[i] = 0; + + SSPI_ExpandBuffer(&ctx->outraw, 8192); + SSPI_ExpandBuffer(&ctx->outcrypt, 65536); + SSPI_ExpandBuffer(&ctx->inraw, 8192); + SSPI_ExpandBuffer(&ctx->incrypt, 65536); + + if (isserver) + SSPI_GenServerCredentials(ctx); + else + SSPI_Handshake(ctx); //begin the initial handshake now + return ctx; +} + +void DTLS_DestroyContext(void *vctx) +{ + SSPI_Close(vctx); +} + + +neterr_t DTLS_Transmit(void *ctx, const qbyte *data, size_t datasize) +{ + int ret; + sslfile_t *f = (sslfile_t *)ctx; + +//Con_Printf("DTLS_Transmit: %i\n", datasize); + + //sspi likes writing over the source data. make sure nothing is hurt by copying it out first. + f->outraw.avail = 0; + SSPI_CopyIntoBuffer(&f->outraw, data, datasize, true); + + if (f->handshaking) + { + SSPI_Handshake(f); + + if (f->handshaking == HS_ERROR) + ret = NETERR_DISCONNECTED; + ret = NETERR_CLOGGED; //not ready yet + } + else + { + SSPI_Encode(f); + ret = NETERR_SENT; + } + + return ret; +} + +neterr_t DTLS_Received(void *ctx, qbyte *data, size_t datasize) +{ + int ret; + sslfile_t *f = (sslfile_t *)ctx; + +//Con_Printf("DTLS_Received: %i\n", datasize); + + f->incrypt.data = data; + f->incrypt.avail = f->incrypt.datasize = datasize; + + if (f->handshaking) + { + SSPI_Handshake(f); + ret = NETERR_CLOGGED; //not ready yet + + if (f->handshaking == HS_ERROR) + ret = NETERR_DISCONNECTED; + } + else + { + SSPI_Decode(f); + ret = NETERR_SENT; + + memcpy(net_message_buffer, f->inraw.data, f->inraw.avail); + net_message.cursize = f->inraw.avail; + f->inraw.avail = 0; + + net_message_buffer[net_message.cursize] = 0; +// Con_Printf("returning %i bytes: %s\n", net_message.cursize, net_message_buffer); + } + f->incrypt.data = NULL; + return ret; +} +neterr_t DTLS_Timeouts(void *ctx) +{ + sslfile_t *f = (sslfile_t *)ctx; + if (f->handshaking) + { +// SSPI_Handshake(f); + return NETERR_CLOGGED; + } + return NETERR_SENT; +} +#endif + #endif diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index f779112a..02656b1f 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -97,13 +97,16 @@ cvar_t net_enabled = CVARD("net_enabled", "1", "If 0, disables all network #if defined(TCPCONNECT) && !defined(CLIENTONLY) cvar_t net_enable_qizmo = CVARD("net_enable_qizmo", "1", "Enables compatibility with qizmo's tcp connections serverside. Frankly, using sv_port_tcp without this is a bit pointless."); cvar_t net_enable_qtv = CVARD("net_enable_qtv", "1", "Listens for qtv proxies, or clients using the qtvplay command."); -cvar_t net_enable_tls = CVARD("net_enable_tls", "1", "If enabled, binary data sent to a tcp connection will be interpretted as a tls handshake (enabling https or wss over the same tcp port."); +cvar_t net_enable_tls = CVARD("net_enable_tls", "1", "If enabled, binary data sent to a non-tls tcp port will be interpretted as a tls handshake (enabling https or wss over the same tcp port."); cvar_t net_enable_http = CVARD("net_enable_http", "1", "If enabled, tcp ports will accept http clients, potentially serving large files which could distrupt gameplay."); cvar_t net_enable_websockets = CVARD("net_enable_websockets", "1", "If enabled, tcp ports will accept websocket game clients."); cvar_t net_enable_webrtcbroker = CVARD("net_enable_webrtcbroker", "1", "If 1, tcp ports will accept websocket connections from clients trying to broker direct webrtc connections. This should be low traffic, but might involve a lot of mostly-idle connections."); #endif -extern cvar_t sv_public, sv_listen_qw, sv_listen_nq, sv_listen_dp, sv_listen_q3; +extern cvar_t sv_public, sv_listen_qw, sv_listen_nq, sv_listen_dp; +#ifdef QWOVERQ3 +extern cvar_t sv_listen_q3; +#endif #define MAX_LOOPBACK 64 typedef struct @@ -121,6 +124,14 @@ typedef struct } loopback_t; loopback_t loopbacks[2]; + + +#ifdef HAVE_DTLS +static neterr_t FTENET_DTLS_SendPacket(ftenet_connections_t *col, int length, const void *data, netadr_t *to); +#endif + + + //============================================================================= int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s) @@ -234,7 +245,6 @@ qboolean NET_AddrIsReliable(netadr_t *adr) //hints that the protocol is reliable case NP_TLS: case NP_WS: case NP_WSS: - case NP_IRC: return true; } } @@ -495,7 +505,6 @@ char *NET_AdrToString (char *s, int len, netadr_t *a) case NP_TLS: prot = "tls://"; break; case NP_WS: prot = "ws://"; break; case NP_WSS: prot = "wss://"; break; - case NP_IRC: prot = "irc://"; break; case NP_NATPMP: prot = "natpmp://"; break; } @@ -679,7 +688,6 @@ char *NET_BaseAdrToString (char *s, int len, netadr_t *a) case NP_TLS: prot = "tls://"; break; case NP_WS: prot = "ws://"; break; case NP_WSS: prot = "wss://"; break; - case NP_IRC: prot = "irc://"; break; case NP_NATPMP: prot = "natpmp://"; break; } @@ -1141,11 +1149,11 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num a->prot = NP_STREAM; return true; } - if (!strncmp (s, "ws://", 7)) + if (!strncmp (s, "ws://", 5)) { //make sure that the rest of the address is a valid ip address (4 or 6) - if (!NET_StringToSockaddr (s+6, defaultport, &sadr[0], NULL, NULL)) + if (!NET_StringToSockaddr (s+5, defaultport, &sadr[0], NULL, NULL)) { a->type = NA_INVALID; return false; @@ -1155,7 +1163,7 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num a->prot = NP_WS; return true; } - if (!strncmp (s, "wss://", 7)) + if (!strncmp (s, "wss://", 6)) { //make sure that the rest of the address is a valid ip address (4 or 6) @@ -1171,9 +1179,10 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num } if (!strncmp (s, "dtls://", 7)) { +#ifdef HAVE_DTLS //make sure that the rest of the address is a valid ip address (4 or 6) - if (!NET_StringToSockaddr (s+6, defaultport, &sadr[0], NULL, NULL)) + if (!NET_StringToSockaddr (s+7, defaultport, &sadr[0], NULL, NULL)) { a->type = NA_INVALID; return false; @@ -1182,6 +1191,9 @@ size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t num SockadrToNetadr (&sadr[0], a); a->prot = NP_DTLS; return true; +#else + return false; +#endif } if (!strncmp (s, "tls://", 6)) { @@ -2217,6 +2229,144 @@ ftenet_generic_connection_t *FTENET_NATPMP_EstablishConnection(qboolean isserver } #endif +#ifdef HAVE_DTLS +struct dtlspeer_s +{ + ftenet_connections_t *col; + void *dtlsstate; + netadr_t addr; + float timeout; + + struct dtlspeer_s *next; + struct dtlspeer_s **link; +}; + +void NET_DTLS_Timeouts(ftenet_connections_t *col) +{ + struct dtlspeer_s *peer; + if (!col) + return; + for (peer = col->dtls; peer; peer = peer->next) + { + DTLS_Timeouts(peer->dtlsstate); + } +} + +static neterr_t NET_SendPacketCol (ftenet_connections_t *collection, int length, const void *data, netadr_t *to); +static neterr_t FTENET_DTLS_DoSendPacket(void *cbctx, const qbyte *data, size_t length) +{ //callback that does the actual sending + struct dtlspeer_s *peer = cbctx; + return NET_SendPacketCol(peer->col, length, data, &peer->addr); +} +qboolean NET_DTLS_Create(ftenet_connections_t *col, netadr_t *to) +{ + struct dtlspeer_s *peer; + if (to->prot != NP_DGRAM) + return false; + for (peer = col->dtls; peer; peer = peer->next) + { + if (NET_CompareAdr(&peer->addr, to)) + break; + } + if (!peer) + { + char hostname[256]; + peer = Z_Malloc(sizeof(*peer)); + peer->addr = *to; + peer->col = col; + + peer->dtlsstate = DTLS_CreateContext(NET_BaseAdrToString(hostname, sizeof(hostname), to), peer, FTENET_DTLS_DoSendPacket, col->islisten); + Sys_Printf("Created %p\n", peer->dtlsstate); + + if (peer->next) + peer->next->link = &peer->next; + peer->link = &col->dtls; + peer->next = col->dtls; + col->dtls = peer; + } + return true; +} +static void NET_DTLS_DisconnectPeer(ftenet_connections_t *col, struct dtlspeer_s *peer) +{ +// Sys_Printf("Destroy %p\n", peer->dtlsstate); + + if (peer->next) + peer->next->link = peer->link; + *peer->link = peer->next; + + DTLS_DestroyContext(peer->dtlsstate); + Z_Free(peer); +} +qboolean NET_DTLS_Disconnect(ftenet_connections_t *col, netadr_t *to) +{ + struct dtlspeer_s *peer; + netadr_t n = *to; + if (!col || (to->prot != NP_DGRAM && to->prot != NP_DTLS)) + return false; + n.prot = NP_DGRAM; + for (peer = col->dtls; peer; peer = peer->next) + { + if (NET_CompareAdr(&peer->addr, &n)) + { + NET_DTLS_DisconnectPeer(col, peer); + break; + } + } + return peer?true:false; +} +static neterr_t FTENET_DTLS_SendPacket(ftenet_connections_t *col, int length, const void *data, netadr_t *to) +{ + struct dtlspeer_s *peer; + to->prot = NP_DGRAM; + for (peer = col->dtls; peer; peer = peer->next) + { + if (NET_CompareAdr(&peer->addr, to)) + break; + } + to->prot = NP_DTLS; + if (peer) + return DTLS_Transmit(peer->dtlsstate, data, length); + else + return NETERR_NOROUTE; +} + +qboolean NET_DTLS_Decode(ftenet_connections_t *col) +{ + struct dtlspeer_s *peer; + for (peer = col->dtls; peer; peer = peer->next) + { + if (NET_CompareAdr(&peer->addr, &net_from)) + { + switch(DTLS_Received(peer->dtlsstate, net_message.data, net_message.cursize)) + { + case NETERR_DISCONNECTED: + Sys_Printf("disconnected %p\n", peer->dtlsstate); + NET_DTLS_DisconnectPeer(col, peer); + break; + case NETERR_NOROUTE: + return false; //not a valid dtls packet. + default: + case NETERR_CLOGGED: + //ate it + net_message.cursize = 0; + break; + case NETERR_SENT: + //we decoded it properly + break; + } + net_from.prot = NP_DTLS; + return true; + } + } + return false; +} +#endif + + + + + + static qboolean FTENET_AddToCollection_Ptr(ftenet_connections_t *col, const char *name, ftenet_generic_connection_t *(*establish)(qboolean isserver, const char *address, netadr_t adr), qboolean islisten, const char *address, netadr_t *adr) { int count = 0; @@ -2300,10 +2450,10 @@ qboolean FTENET_AddToCollection(ftenet_connections_t *col, const char *name, con if (adr[i].prot == NP_DGRAM && adr[i].type == NA_LOOPBACK) establish[i] = FTENET_Loop_EstablishConnection; else #endif #ifdef HAVE_IPV4 - if (adr[i].prot == NP_DGRAM && adr[i].type == NA_IP) establish[i] = FTENET_Datagram_EstablishConnection; else + if ((adr[i].prot == NP_DGRAM) && adr[i].type == NA_IP) establish[i] = FTENET_Datagram_EstablishConnection; else #endif #ifdef HAVE_IPV6 - if (adr[i].prot == NP_DGRAM && adr[i].type == NA_IPV6) establish[i] = FTENET_Datagram_EstablishConnection; else + if ((adr[i].prot == NP_DGRAM) && adr[i].type == NA_IPV6) establish[i] = FTENET_Datagram_EstablishConnection; else #endif #ifdef USEIPX if (adr[i].prot == NP_DGRAM && adr[i].type == NA_IPX) establish[i] = FTENET_Datagram_EstablishConnection; else @@ -4021,7 +4171,8 @@ static int QDECL TLSPromoteRead (struct vfsfile_s *file, void *buffer, int bytes if (bytestoread > net_message.cursize) bytestoread = net_message.cursize; memcpy(buffer, net_message_buffer, bytestoread); - net_message.cursize = 0; + net_message.cursize -= bytestoread; + memmove(net_message_buffer, net_message_buffer+bytestoread, net_message.cursize); return bytestoread; } #endif @@ -4120,7 +4271,8 @@ qboolean FTENET_TCPConnect_GetPacket(ftenet_generic_connection_t *gcon) { Con_Printf ("tcp peer %s closed connection\n", NET_AdrToString (adr, sizeof(adr), &st->remoteaddr)); closesvstream: - VFS_CLOSE(st->clientstream); + if (st->clientstream) + VFS_CLOSE(st->clientstream); st->clientstream = NULL; continue; } @@ -4143,13 +4295,16 @@ closesvstream: memcpy(net_message_buffer, st->inbuffer, st->inlen); net_message.cursize = st->inlen; //wrap the stream now - st->clientstream = FS_OpenSSL(NULL, st->clientstream, true, false); + st->clientstream = FS_OpenSSL(NULL, st->clientstream, true); st->remoteaddr.prot = NP_TLS; - //try and reclaim it all - st->inlen = VFS_READ(st->clientstream, st->inbuffer, sizeof(st->inbuffer)-1); - //make sure we actually read from the proper stream again - stream->ReadBytes = realread; - if (net_message.cursize) + if (st->clientstream) + { + //try and reclaim it all + st->inlen = VFS_READ(st->clientstream, st->inbuffer, sizeof(st->inbuffer)-1); + //make sure we actually read from the proper stream again + stream->ReadBytes = realread; + } + if (!st->clientstream || net_message.cursize) goto closesvstream; //something cocked up. we didn't give the tls stream all the data. net_message.cursize = 0; continue; @@ -4564,7 +4719,7 @@ closesvstream: #ifdef HAVE_SSL if (con->tls && st->clientstream) //if we're meant to be using tls, wrap the stream in a tls connection { - st->clientstream = FS_OpenSSL(NULL, st->clientstream, true, false); + st->clientstream = FS_OpenSSL(NULL, st->clientstream, true); /*sockadr doesn't contain transport info, so fix that up here*/ st->remoteaddr.prot = NP_TLS; } @@ -4890,7 +5045,7 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(qboolean isse #ifdef HAVE_SSL if (newcon->tls) //if we're meant to be using tls, wrap the stream in a tls connection - newcon->tcpstreams->clientstream = FS_OpenSSL(address, newcon->tcpstreams->clientstream, false, false); + newcon->tcpstreams->clientstream = FS_OpenSSL(address, newcon->tcpstreams->clientstream, false); #endif //send the qizmo greeting. @@ -6202,21 +6357,20 @@ int NET_GetPacket (netsrc_t netsrc, int firstsock) while (firstsock < MAX_CONNECTIONS) { - if (!collection->conn[firstsock]) - break; - if (collection->conn[firstsock]->GetPacket(collection->conn[firstsock])) - { - if (net_fakeloss.value) + if (collection->conn[firstsock]) + if (collection->conn[firstsock]->GetPacket(collection->conn[firstsock])) { - if (frandom () < net_fakeloss.value) - continue; - } + if (net_fakeloss.value) + { + if (frandom () < net_fakeloss.value) + continue; + } - collection->bytesin += net_message.cursize; - collection->packetsin += 1; - net_from.connum = firstsock+1; - return firstsock; - } + collection->bytesin += net_message.cursize; + collection->packetsin += 1; + net_from.connum = firstsock+1; + return firstsock; + } firstsock += 1; } @@ -6254,32 +6408,11 @@ int NET_LocalAddressForRemote(ftenet_connections_t *collection, netadr_t *remote return collection->conn[remote->connum-1]->GetLocalAddresses(collection->conn[remote->connum-1], &adrflags, local, 1); } -neterr_t NET_SendPacket (netsrc_t netsrc, int length, const void *data, netadr_t *to) +static neterr_t NET_SendPacketCol (ftenet_connections_t *collection, int length, const void *data, netadr_t *to) { neterr_t err; -// char buffer[64]; - ftenet_connections_t *collection; int i; - if (netsrc == NS_SERVER) - { -#ifdef CLIENTONLY - Sys_Error("NET_GetPacket: Bad netsrc"); - return NETERR_NOROUTE; -#else - collection = svs.sockets; -#endif - } - else - { -#ifdef SERVERONLY - Sys_Error("NET_GetPacket: Bad netsrc"); - return NETERR_NOROUTE; -#else - collection = cls.sockets; -#endif - } - if (!collection) return NETERR_NOROUTE; @@ -6338,6 +6471,35 @@ neterr_t NET_SendPacket (netsrc_t netsrc, int length, const void *data, netadr_t return NETERR_NOROUTE; } +neterr_t NET_SendPacket (netsrc_t netsrc, int length, const void *data, netadr_t *to) +{ + ftenet_connections_t *collection; + + if (netsrc == NS_SERVER) + { +#ifdef CLIENTONLY + Sys_Error("NET_GetPacket: Bad netsrc"); + return NETERR_NOROUTE; +#else + collection = svs.sockets; +#endif + } + else + { +#ifdef SERVERONLY + Sys_Error("NET_GetPacket: Bad netsrc"); + return NETERR_NOROUTE; +#else + collection = cls.sockets; +#endif + } +#ifdef HAVE_DTLS + if (to->prot == NP_DTLS) + return FTENET_DTLS_SendPacket(collection, length, data, to); +#endif + return NET_SendPacketCol (collection, length, data, to); +} + qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char *host, qboolean islisten) { netadr_t adr; @@ -6347,11 +6509,15 @@ qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char switch(adr.prot) { case NP_DTLS: +#ifdef HAVE_DTLS +// if (FTENET_DTLS_Create(collection, &adr) == NETERR_NOROUTE) +/// return false; + break; +#endif case NP_WS: case NP_WSS: case NP_TLS: case NP_STREAM: - case NP_IRC: if (!FTENET_AddToCollection(collection, routename, host, adr.type, adr.prot, islisten)) return false; Con_Printf("Establishing connection to %s\n", host); @@ -6496,6 +6662,15 @@ void NET_PrintConnectionsStatus(ftenet_connections_t *collection) if (collection->conn[i]->PrintStatus) collection->conn[i]->PrintStatus(collection->conn[i]); } + +#ifdef HAVE_DTLS + { + struct dtlspeer_s *dtls; + char adr[64]; + for (dtls = collection->dtls; dtls; dtls = dtls->next) + Con_Printf("dtls: %s\n", NET_AdrToString(adr, sizeof(adr), &dtls->addr)); + } +#endif } //============================================================================= @@ -6977,7 +7152,7 @@ void NET_ClientPort_f(void) qboolean NET_WasSpecialPacket(netsrc_t netsrc) { -#ifdef HAVE_NATPMP +#if defined(HAVE_NATPMP) ftenet_connections_t *collection = NULL; if (netsrc == NS_SERVER) { @@ -6991,16 +7166,18 @@ qboolean NET_WasSpecialPacket(netsrc_t netsrc) collection = cls.sockets; #endif } + +#ifdef HAVE_NATPMP + if (NET_Was_NATPMP(collection)) + return true; +#endif #endif #ifdef SUPPORT_ICE if (ICE_WasStun(netsrc)) return true; #endif -#ifdef HAVE_NATPMP - if (NET_Was_NATPMP(collection)) - return true; -#endif + return false; } @@ -7065,6 +7242,11 @@ void NET_Init (void) Net_Master_Init(); } #ifndef SERVERONLY +void NET_CloseClient(void) +{ //called by disconnect console command + FTENET_CloseCollection(cls.sockets); + cls.sockets = NULL; +} void NET_InitClient(qboolean loopbackonly) { const char *port; @@ -7201,6 +7383,16 @@ void SVNET_RegisterCvars(void) Cvar_Register (&sv_port_natpmp, "networking"); sv_port_natpmp.restriction = RESTRICT_MAX; #endif + + +#if defined(TCPCONNECT) && !defined(CLIENTONLY) + Cvar_Register (&net_enable_qizmo, "networking"); + Cvar_Register (&net_enable_qtv, "networking"); + Cvar_Register (&net_enable_tls, "networking"); + Cvar_Register (&net_enable_http, "networking"); + Cvar_Register (&net_enable_websockets, "networking"); + Cvar_Register (&net_enable_webrtcbroker, "networking"); +#endif } void NET_CloseServer(void) @@ -7211,7 +7403,11 @@ void NET_CloseServer(void) void NET_InitServer(void) { - if (sv_listen_nq.value || sv_listen_dp.value || sv_listen_qw.value || sv_listen_q3.ival) + if (sv_listen_nq.value || sv_listen_dp.value || sv_listen_qw.value +#ifdef QWOVERQ3 + || sv_listen_q3.ival +#endif + ) { if (!svs.sockets) { diff --git a/engine/common/netinc.h b/engine/common/netinc.h index 150692ff..b0bd10f7 100644 --- a/engine/common/netinc.h +++ b/engine/common/netinc.h @@ -292,6 +292,17 @@ typedef struct ftenet_generic_connection_s { #endif } ftenet_generic_connection_t; +#ifdef HAVE_DTLS +void *DTLS_CreateContext(char *remotehost, void *cbctx, neterr_t(*push)(void *cbctx, const qbyte *data, size_t datasize), qboolean isserver); //if remotehost is null then their certificate will not be validated. +void DTLS_DestroyContext(void *ctx); +neterr_t DTLS_Transmit(void *ctx, const qbyte *data, size_t datasize); +neterr_t DTLS_Received(void *ctx, qbyte *data, size_t datasize); +neterr_t DTLS_Timeouts(void *ctx); +qboolean DTLS_HasServerCertificate(void); +#endif + + + #define MAX_CONNECTIONS 8 typedef struct ftenet_connections_s { @@ -306,6 +317,10 @@ typedef struct ftenet_connections_s float bytesinrate; float bytesoutrate; ftenet_generic_connection_t *conn[MAX_CONNECTIONS]; + +#ifdef HAVE_DTLS + struct dtlspeer_s *dtls; //linked list. linked lists are shit, but at least it keeps pointers valid when things are resized. +#endif } ftenet_connections_t; void ICE_Tick(void); @@ -318,7 +333,7 @@ void FTENET_CloseCollection(ftenet_connections_t *col); qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, netproto_t addrprot, qboolean islisten); int NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_generic_connection_s **con, unsigned int *adrflags, netadr_t *addresses, int maxaddresses); -vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server, qboolean datagram); +vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server); #ifdef HAVE_PACKET vfsfile_t *FS_OpenTCPSocket(SOCKET socket, qboolean conpending, const char *peername); //conpending allows us to reject any writes until the connection has succeeded #endif diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 1852b522..b0ae2fc9 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -1085,7 +1085,7 @@ qintptr_t VARGS Plug_Net_SetTLSClient(void *offset, quintptr_t mask, const qintp return -2; } - stream->vfs = FS_OpenSSL(VM_POINTER(arg[1]), stream->vfs, false, false); + stream->vfs = FS_OpenSSL(VM_POINTER(arg[1]), stream->vfs, false); if (!stream->vfs) { Plug_Net_Close_Internal(handle); diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 4761cd6b..90620163 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -98,11 +98,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif +#define PROTOCOL_VERSION_VARLENGTH (('v'<<0) + ('l'<<8) + ('e'<<16) + ('n' << 24)) //variable length handshake + #define PROTOCOL_VERSION_FTE (('F'<<0) + ('T'<<8) + ('E'<<16) + ('X' << 24)) //fte extensions. #define PROTOCOL_VERSION_FTE2 (('F'<<0) + ('T'<<8) + ('E'<<16) + ('2' << 24)) //fte extensions. #define PROTOCOL_VERSION_HUFFMAN (('H'<<0) + ('U'<<8) + ('F'<<16) + ('F' << 24)) //packet compression -#define PROTOCOL_VERSION_VARLENGTH (('v'<<0) + ('l'<<8) + ('e'<<16) + ('n' << 24)) //variable length handshake #define PROTOCOL_VERSION_FRAGMENT (('F'<<0) + ('R'<<8) + ('A'<<16) + ('G' << 24)) //supports fragmentation/packets larger than 1450 +#ifdef HAVE_DTLS +#define PROTOCOL_VERSION_DTLSUPGRADE (('D'<<0) + ('T'<<8) + ('L'<<16) + ('S' << 24)) //server supports dtls. clients should dtlsconnect THEN continue connecting (also allows dtls rcon!). +#endif #define PROTOCOL_INFO_GUID (('G'<<0) + ('U'<<8) + ('I'<<16) + ('D' << 24)) //globally 'unique' client id info. @@ -1733,3 +1737,5 @@ typedef struct q1usercmd_s #define E5_EXTEND4 (1<<31) #define E5_ALLUNUSED (E5_UNUSED27|E5_UNUSED28|E5_UNUSED29|E5_UNUSED30) +#define E5_SERVERPRIVATE (E5_EXTEND1|E5_EXTEND2|E5_EXTEND3|E5_EXTEND4) +#define E5_SERVERREMOVE E5_EXTEND1 diff --git a/engine/dotnet2005/ftequake.sln b/engine/dotnet2005/ftequake.sln index 69339861..e81f513e 100644 --- a/engine/dotnet2005/ftequake.sln +++ b/engine/dotnet2005/ftequake.sln @@ -556,7 +556,8 @@ Global {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Debug|Win32.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Debug|x64.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Debug|x64.Build.0 = Debug Dedicated Server|x64 - {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLDebug|Win32.ActiveCfg = Debug Dedicated Server|x64 + {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLDebug|Win32.ActiveCfg = Debug Dedicated Server|Win32 + {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLDebug|Win32.Build.0 = Debug Dedicated Server|Win32 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLDebug|x64.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLRelease|Win32.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.GLRelease|x64.ActiveCfg = Debug Dedicated Server|x64 @@ -580,7 +581,7 @@ Global {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Release|Win32.ActiveCfg = Release Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Release|x64.ActiveCfg = Release Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.Release|x64.Build.0 = Release Dedicated Server|x64 - {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.VkDebug|Win32.ActiveCfg = Debug Dedicated Server|x64 + {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.VkDebug|Win32.ActiveCfg = Debug Dedicated Server|Win32 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.VkDebug|x64.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.VkRelease|Win32.ActiveCfg = Debug Dedicated Server|x64 {482A886A-5755-4DAE-AD5F-D7CD4A990F9E}.VkRelease|x64.ActiveCfg = Debug Dedicated Server|x64 diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index 37c6e6bc..7ec4a5da 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -7250,6 +7250,53 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "}\n" "#endif\n" +}, +#endif +#ifdef GLQUAKE +{QR_OPENGL, 110, "itemtimer", +"!!permu FOG\n" + +"#include \"sys/defs.h\"\n" +"#include \"sys/fog.h\"\n" + +"varying vec2 tc;\n" +"varying vec4 vc;\n" + +"#ifdef VERTEX_SHADER\n" +"void main ()\n" +"{\n" +"tc = v_texcoord;\n" +"vc = v_colour;\n" +"gl_Position = ftetransform();\n" +"}\n" +"#endif\n" + + +"#ifdef FRAGMENT_SHADER\n" +"void main ()\n" +"{\n" +"gl_FragColor = vec4(0.5,0.5,0.5,1);//texture2D(s_diffuse, tc.xy);\n" + +"vec2 st = (tc-floor(tc)) - 0.5;\n" +"st *= 2.0;\n" +"float dist = sqrt(dot(st,st));\n" + +"float ring = 1.0 + smoothstep(0.9, 1.0, dist)\n" +"- smoothstep(0.8, 0.9, dist);\n" + +//fade out the rim +"if ((atan(st.t, st.s)+3.14)/6.28 > vc.a)\n" +"gl_FragColor.a *= 0.25;\n" +"gl_FragColor.rgb *= mix(vc.rgb, vec3(0.0), ring);\n" +//gl_FragColor.a; + +//and finally hide it all if we're fogged. +"#ifdef FOG\n" +"gl_FragColor = fog4additive(gl_FragColor);\n" +"#endif\n" +"}\n" +"#endif\n" + }, #endif #ifdef GLQUAKE diff --git a/engine/http/ftpserver.c b/engine/http/ftpserver.c index 39bbf8e9..0987bb88 100644 --- a/engine/http/ftpserver.c +++ b/engine/http/ftpserver.c @@ -1485,8 +1485,10 @@ unsigned long _true = true; cl->controlaf = 1; else if (((struct sockaddr *)&from)->sa_family == AF_INET6) cl->controlaf = 2; +#ifdef USEIPX else if (((struct sockaddr *)&from)->sa_family == AF_IPX) cl->controlaf = 11; +#endif else cl->controlaf = 0; diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index 6e03f4f0..c18e78fb 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -1061,7 +1061,7 @@ void HTTPDL_Establish(struct dl_download *dl) if (https) { //https has an extra ssl/tls layer between tcp and http. - con->stream = FS_OpenSSL(con->server, con->stream, false, false); + con->stream = FS_OpenSSL(con->server, con->stream, false); con->secure = true; } #endif diff --git a/engine/http/iwebiface.c b/engine/http/iwebiface.c index 396507f6..d4875de4 100644 --- a/engine/http/iwebiface.c +++ b/engine/http/iwebiface.c @@ -601,6 +601,9 @@ void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority { return NULL; } +void Sys_WaitOnThread(void *thread) +{ +} qboolean FS_Remove(const char *fname, enum fs_relative relativeto) { return false; diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 09a47c43..dbf63ed0 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -2948,6 +2948,8 @@ static void QCC_PR_ExpandStrCat(char **buffer, size_t *bufferlen, size_t *buffer static char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t retbufsize) { + if (constname[0] != '_' || constname[1] != '_') + return NULL; if (!strcmp(constname, "__TIME__")) { time_t long_time; @@ -2969,7 +2971,7 @@ static char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t } if (!strcmp(constname, "__QCCVER__")) { - return "FTEQCC "__DATE__","__TIME__""; + return "\"FTEQCC "__DATE__","__TIME__"\""; } if (!strcmp(constname, "__FILE__")) { @@ -2986,7 +2988,7 @@ static char *QCC_PR_CheckBuiltinCompConst(char *constname, char *retbuf, size_t QC_snprintfz(retbuf, retbufsize, "\"%i\"", pr_source_line); return retbuf; } - if (!strcmp(constname, "__FUNC__")) + if (!strcmp(constname, "__FUNC__") || !strcmp(constname, "__func__")) { QC_snprintfz(retbuf, retbufsize, "\"%s\"",pr_scope?pr_scope->name:""); return retbuf; diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index 467059cc..ae01f41b 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -56,6 +56,11 @@ void AddSourceFile(const char *parentsrc, const char *filename); #define SCI_GETANCHOR 2009 #define SCI_SETSAVEPOINT 2014 #define SCI_GETCURLINE 2027 +#define SCI_CONVERTEOLS 2029 +#define SC_EOL_CRLF 0 +#define SC_EOL_CR 1 +#define SC_EOL_LF 2 +#define SCI_SETEOLMODE 2031 #define SCI_SETCODEPAGE 2037 #define SCI_MARKERDEFINE 2040 #define SCI_MARKERSETFORE 2041 @@ -114,6 +119,7 @@ void AddSourceFile(const char *parentsrc, const char *filename); #define SCI_BRACEHIGHLIGHT 2351 #define SCI_BRACEBADLIGHT 2352 #define SCI_BRACEMATCH 2353 +#define SCI_SETVIEWEOL 2356 #define SCI_ANNOTATIONSETTEXT 2540 #define SCI_ANNOTATIONGETTEXT 2541 #define SCI_ANNOTATIONSETSTYLE 2542 @@ -1295,6 +1301,8 @@ enum { IDM_DEBUG_TOGGLEBREAK, IDM_ENCODING_PRIVATEUSE, IDM_ENCODING_DEPRIVATEUSE, + IDM_ENCODING_UNIX, + IDM_ENCODING_WINDOWS, IDM_CREATEINSTALLER_WINDOWS, IDM_CREATEINSTALLER_ANDROID, @@ -1816,6 +1824,15 @@ void EditorMenu(editor_t *editor, WPARAM wParam) GUI_Recode(editor, UTF_ANSI); break; + case IDM_ENCODING_UNIX: + SendMessage(editor->editpane, SCI_CONVERTEOLS, SC_EOL_LF, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0); + break; + case IDM_ENCODING_WINDOWS: + SendMessage(editor->editpane, SCI_CONVERTEOLS, SC_EOL_CRLF, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0); + break; + default: GenericMenu(wParam); break; @@ -2416,7 +2433,6 @@ static void EditorReload(editor_t *editor) char *file; unsigned int flen; pbool dofree; - rawfile = QCC_ReadFile(editor->filename, NULL, 0, &flensz); flen = flensz; @@ -2427,6 +2443,50 @@ static void EditorReload(editor_t *editor) if (editor->scintilla) { + int endings = 0; + char *e, *stop; + for (e = file, stop=file+flen; e < stop; ) + { + if (*e == '\r') + { + e++; + if (*e == '\n') + { + e++; + endings |= 4; + } + else + endings |= 2; + } + else if (*e == '\n') + { + e++; + endings |= 1; + } + else + e++; + } + switch(endings) + { + case 0: //new file with no endings, default to windows on windows. + case 4: //windows + SendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_CRLF, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0); + break; + case 1: //unix + SendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_LF, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0); + break; + case 2: //mac. traditionally qccs have never supported this. one of the mission packs has a \r in the middle of some single-line comment. + SendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_CR, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, false, 0); + break; + default: //panic! everyone panic! + SendMessage(editor->editpane, SCI_SETEOLMODE, SC_EOL_LF, 0); + SendMessage(editor->editpane, SCI_SETVIEWEOL, true, 0); + break; + } + // SendMessage(editor->editpane, SCI_SETTEXT, 0, (LPARAM)file); // SendMessage(editor->editpane, SCI_SETUNDOCOLLECTION, 0, 0); SendMessage(editor->editpane, SCI_SETTEXT, 0, (LPARAM)file); @@ -5301,6 +5361,8 @@ static LRESULT CALLBACK MainWndProc(HWND hWnd,UINT message, AppendMenu(m, MF_SEPARATOR, 0, NULL); AppendMenu(m, 0, IDM_ENCODING_PRIVATEUSE, "Convert to UTF-8"); AppendMenu(m, 0, IDM_ENCODING_DEPRIVATEUSE, "Convert to Quake encoding"); + AppendMenu(m, 0, IDM_ENCODING_UNIX, "Convert to Unix Endings"); + AppendMenu(m, 0, IDM_ENCODING_WINDOWS, "Convert to Dos Endings"); AppendMenu(rootmenu, MF_POPUP, (UINT_PTR)(m = windowmenu = CreateMenu()), "&Window"); AppendMenu(m, 0, IDM_CASCADE, "Cascade"); diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 40859e0c..954f8021 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -9345,7 +9345,7 @@ static void QCBUILTIN PF_runclientphys(pubprogfuncs_t *prinst, struct globalvars pmove.cmd.upmove = bound(-32767, (pr_global_struct->input_movevalues)[2], 32767); pmove.cmd.buttons = pr_global_struct->input_buttons; - pmove.safeorigin_known = true; + pmove.safeorigin_known = progstype != PROG_QW; VectorCopy(ent->v->oldorigin, pmove.safeorigin); VectorCopy(ent->v->origin, pmove.origin); VectorCopy(ent->v->velocity, pmove.velocity); diff --git a/engine/server/server.h b/engine/server/server.h index 606165ff..e2171047 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -299,6 +299,20 @@ typedef struct } server_t; void SV_WipeServerState(void); +/* +#define CS_EMPTY 0 +#define CS_ZOMBIE (1u<<0) //just stops the slot from being reused for a bit. +#define CS_CLUSTER (1u<<1) //is managed by the cluster host (and will appear on scoreboards). +#define CS_SPAWNED (1u<<2) //has an active entity. +#define CS_ACTIVE (1u<<3) //has a connection + +#define cs_free (CS_EMPTY) +#define cs_zombie (CS_ZOMBIE) +#define cs_loadzombie (CS_SPAWNED) +#define cs_connected (CS_ACTIVE) +#define cs_spawned (CS_ACTIVE|CS_SPAWNED) +*/ + typedef enum { cs_free, // can be reused for a new connection @@ -413,6 +427,7 @@ enum #define STUFFCMD_IGNOREINDEMO ( 1<<0) // do not put in mvd demo #define STUFFCMD_DEMOONLY ( 1<<1) // put in mvd demo only +#define STUFFCMD_BROADCAST ( 1<<2) // everyone sees it. typedef struct client_s { @@ -545,14 +560,7 @@ typedef struct client_s //true/false/persist unsigned int penalties; -/* qbyte ismuted; - qbyte iscuffed; - qbyte iscrippled; - qbyte isdeaf; - qbyte islagged; - qbyte isvip; -*/ - qbyte istobeloaded; //loadgame creates place holders for clients to connect to. Effectivly loading a game reconnects all clients, but has precreated ents. + qbyte istobeloaded; //loadgame creates place holders for clients to connect to. Effectivly loading a game reconnects all clients, but has precreated ents. double floodprotmessage; double lastspoke; @@ -1092,6 +1100,10 @@ void SV_FullClientUpdate (client_t *client, client_t *to); void SV_GeneratePublicUserInfo(int pext, client_t *cl, char *info, int infolength); char *SV_PlayerPublicAddress(client_t *cl); +qboolean SVC_GetChallenge (qboolean respond_dp); +int SV_NewChallenge (void); +client_t *SVC_DirectConnect(void); + int SV_ModelIndex (const char *name); void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index df652e5e..c1bbce82 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -1776,7 +1776,13 @@ static void SV_Status_f (void) float pi, po, bi, bo; int columns = 80; - extern cvar_t sv_listen_qw, sv_listen_nq, sv_listen_dp, sv_listen_q3; + extern cvar_t sv_listen_qw, sv_listen_nq, sv_listen_dp; +#ifdef QWOVERQ3 + extern cvar_t sv_listen_q3; +#endif +#ifdef HAVE_DTLS + extern cvar_t sv_listen_dtls; +#endif #ifndef SERVERONLY if (!sv.state && cls.state >= ca_connected && !cls.demoplayback && cls.protocol == CP_NETQUAKE) @@ -1815,7 +1821,47 @@ static void SV_Status_f (void) 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":""); + switch(svs.gametype) + { +#ifdef Q3SERVER + case GT_QUAKE3: + Con_Printf("client types :%s\n", sv_listen_qw.ival?" Q3":""); + break; +#endif +#ifdef Q2SERVER + case GT_QUAKE2: + Con_Printf("client types :%s\n", sv_listen_qw.ival?" Q2":""); + break; +#endif + default: + Con_Printf("client types :%s", sv_listen_qw.ival?" QW":""); +#ifdef NQPROT + Con_Printf("%s%s", (sv_listen_nq.ival==2)?" -NQ":(sv_listen_nq.ival?" NQ":""), sv_listen_dp.ival?" DP":""); +#endif +#ifdef QWOVERQ3 + if (sv_listen_q3.ival) Con_Printf(" Q3"); +#endif +#ifdef HAVE_DTLS + if (sv_listen_dtls.ival >= 2) + Con_Printf(" +DTLS"); + else if (sv_listen_dtls.ival) + Con_Printf(" DTLS"); +#endif + /*if (net_enable_tls.ival) + Con_Printf(" TLS"); + if (net_enable_http.ival) + Con_Printf(" HTTP"); + if (net_enable_webrtcbroker.ival) + Con_Printf(" WebRTC"); + if (net_enable_websockets.ival) + Con_Printf(" WS"); + if (net_enable_qizmo.ival) + Con_Printf(" QZ"); + if (net_enable_qtv.ival) + Con_Printf(" QTV");*/ + Con_Printf("\n"); + break; + } #ifdef SUBSERVERS if (sv.state == ss_clustermode) { diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index 2e319fb8..8cc1029c 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -1786,17 +1786,12 @@ unsigned int SVDP_CalcDelta(entity_state_t *from, qbyte *frombonedatabase, entit return bits; } -void SVDP_EmitEntityDelta(entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean isnew, qbyte *bonedatabase) +void SVDP_EmitEntityDelta(unsigned int bits, entity_state_t *to, sizebuf_t *msg, qbyte *bonedatabase) { - int bits; + bits &= ~E5_SERVERPRIVATE; - bits = 0; - if (isnew) - bits |= E5_FULLUPDATE; - - //FIXME: this stuff should be outside of this function - //with the whole nack stuff - bits = SVDP_CalcDelta(from, NULL, to, bonedatabase); + if (!bits) + return; if (bits >= 256) bits |= E5_EXTEND1; @@ -1805,9 +1800,6 @@ void SVDP_EmitEntityDelta(entity_state_t *from, entity_state_t *to, sizebuf_t *m if (bits >= 16777216) bits |= E5_EXTEND3; - if (!bits) - return; - MSG_WriteShort(msg, to->number); MSG_WriteByte(msg, bits & 0xFF); if (bits & E5_EXTEND1) @@ -1930,6 +1922,7 @@ void SVDP_EmitEntitiesUpdate (client_t *client, packet_entities_t *to, sizebuf_t int oldindex, newindex; int oldnum, newnum; int oldmax; + int j; // this is the frame that we are going to delta update from if (!client->netchan.incoming_sequence) @@ -1939,11 +1932,59 @@ void SVDP_EmitEntitiesUpdate (client_t *client, packet_entities_t *to, sizebuf_t } else { - client_frame_t *fromframe = &client->frameunion.frames[(client->netchan.incoming_sequence-1) & UPDATE_MASK]; - from = &fromframe->entities; + from = &client->sentents; oldmax = from->num_entities; } + if (to->num_entities) + { + j = to->entities[to->num_entities-1].number+1; + if (j > from->max_entities) + { + from->entities = BZ_Realloc(from->entities, sizeof(*from->entities) * j); + memset(&from->entities[from->max_entities], 0, sizeof(from->entities[0]) * (j - from->max_entities)); + from->max_entities = j; + } + while(j > client->sentents.num_entities) + { + from->entities[from->num_entities].number = 0; + from->num_entities++; + } + } + + //diff the from+to states, flagging any changed state (which is combined with any state from previous packet loss + newindex = 0; + oldindex = 0; + while (newindex < to->num_entities || oldindex < oldmax) + { + newnum = newindex >= to->num_entities ? 0x7fff : to->entities[newindex].number; + oldnum = oldindex >= oldmax ? 0x7fff : from->entities[oldindex].number; + + if (newnum < oldnum) + { // this is a new entity, send it from the baseline... as far as dp understands it... + client->pendingdeltabits[newnum] |= E5_FULLUPDATE | SVDP_CalcDelta(&nullentitystate, NULL, &to->entities[oldindex], to->bonedata); + newindex++; + } + else if (newnum > oldnum) + { // the old entity isn't present in the new message + client->pendingdeltabits[oldnum] = E5_SERVERREMOVE; + oldindex++; + } + else + { // delta update from old position + client->pendingdeltabits[newnum] |= SVDP_CalcDelta(&from->entities[oldindex], NULL/*from->bonedata*/, &to->entities[oldindex], to->bonedata); + if (client->pendingdeltabits[newnum] & E5_SERVERREMOVE) + { //if it got flagged for removal, but its actually a valid entity, then assume that its an outdated remove and just flag it for a full update in case stuff got lost. + client->pendingdeltabits[newnum] &= ~E5_SERVERREMOVE; + client->pendingdeltabits[newnum] |= E5_FULLUPDATE; + } + oldindex++; + newindex++; + } + } + + //loop through all ents and send them as required + // Con_Printf ("frame %i\n", client->netchan.incoming_sequence); MSG_WriteByte(msg, svcdp_entities); @@ -1955,7 +1996,7 @@ void SVDP_EmitEntitiesUpdate (client_t *client, packet_entities_t *to, sizebuf_t //add in the bitmasks of dropped packets. - newindex = 0; +/* newindex = 0; oldindex = 0; //Con_Printf ("---%i to %i ----\n", client->delta_sequence & UPDATE_MASK // , client->netchan.outgoing_sequence & UPDATE_MASK); @@ -1989,7 +2030,7 @@ void SVDP_EmitEntitiesUpdate (client_t *client, packet_entities_t *to, sizebuf_t continue; } } - +*/ MSG_WriteShort(msg, 0x8000); } #endif @@ -3862,6 +3903,7 @@ void SV_Snapshot_Clear(packet_entities_t *pack) numnails = 0; } +#ifdef QWOVERQ3 /* ============= SVQ3Q1_BuildEntityPacket @@ -3876,6 +3918,7 @@ void SVQ3Q1_BuildEntityPacket(client_t *client, packet_entities_t *pack) SV_Snapshot_SetupPVS(client, &cameras); SV_Snapshot_BuildQ1(client, pack, &cameras, client->edict); } +#endif /* ============= diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 9eaf254f..2fa159d1 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -41,6 +41,14 @@ void SV_Tcpport6_Callback(struct cvar_s *var, char *oldvalue); void SV_Port_Callback(struct cvar_s *var, char *oldvalue); void SV_PortIPv6_Callback(struct cvar_s *var, char *oldvalue); void SV_PortIPX_Callback(struct cvar_s *var, char *oldvalue); +#ifdef HAVE_DTLS +void SV_Listen_Dtls_Changed(struct cvar_s *var, char *oldvalue) +{ + if (var->ival) + if (!DTLS_HasCertificate()) + var->ival = 0; //disable the cvar (internally) if we don't have a usable certificate. this allows us to default the cvar to enabled without it breaking otherwise. +} +#endif client_t *host_client; // current client @@ -97,10 +105,15 @@ extern cvar_t sv_allow_splitscreen; cvar_t sv_serverip = CVARD("sv_serverip", "", "Set this cvar to the server's public ip address if the server is behind a firewall and cannot detect its own public address. Providing a port is required if the firewall/nat remaps it, but is otherwise optional."); cvar_t sv_public = CVAR("sv_public", "0"); -cvar_t sv_listen_qw = CVARAF("sv_listen_qw", "1", "sv_listen", 0); +cvar_t sv_listen_qw = CVARAFD("sv_listen_qw", "1", "sv_listen", 0, "Specifies whether normal clients are allowed to connect."); cvar_t sv_listen_nq = CVARD("sv_listen_nq", "2", "Allow new (net)quake clients to connect to the server.\n0 = don't let them in.\n1 = allow them in (WARNING: this allows 'qsmurf' DOS attacks).\n2 = accept (net)quake clients by emulating a challenge (as secure as QW/Q2 but does not fully conform to the NQ protocol)."); cvar_t sv_listen_dp = CVARD("sv_listen_dp", "0", "Allows the server to respond with the DP-specific handshake protocol.\nWarning: this can potentially get confused with quake2, and results in race conditions with both vanilla netquake and quakeworld protocols.\nOn the plus side, DP clients can usually be identified correctly, enabling a model+sound limit boost."); +#ifdef QWOVERQ3 cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0"); +#endif +#ifdef HAVE_DTLS +cvar_t sv_listen_dtls = CVARCD("net_enable_dtls", "", SV_Listen_Dtls_Changed, "Controls serverside dtls support.\n0: dtls blocked, not advertised.\n1: available in desired.\n2: used where possible.\n3: disallow non-dtls clients (sv_port_tcp should be eg tls://[::]:27500 to also disallow unencrypted tcp connections)."); +#endif cvar_t sv_reportheartbeats = CVAR("sv_reportheartbeats", "1"); cvar_t sv_highchars = CVAR("sv_highchars", "1"); cvar_t sv_maxrate = CVAR("sv_maxrate", "30000"); @@ -679,6 +692,10 @@ void SV_DropClient (client_t *drop) //send twice, to cover packetloss a little. Netchan_Transmit (&drop->netchan, termmsg.cursize, termmsg.data, 10000); Netchan_Transmit (&drop->netchan, termmsg.cursize, termmsg.data, 10000); + +#ifdef HAVE_DTLS + NET_DTLS_Disconnect(svs.sockets, &drop->netchan.remote_address); +#endif } if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) //gamecode should do it all for us. @@ -1071,7 +1088,7 @@ Responds with all the info that qplug or qspy can see This message can be up to around 5k with worst case string lengths. ================ */ -void SVC_Status (void) +static void SVC_Status (void) { int displayflags; int i; @@ -1150,7 +1167,7 @@ void SVC_Status (void) } #ifdef NQPROT -void SVC_GetInfo (char *challenge, int fullstatus) +static void SVC_GetInfo (char *challenge, int fullstatus) { //dpmaster support char response[MAX_UDP_PACKET]; @@ -1267,7 +1284,7 @@ void SVC_GetInfo (char *challenge, int fullstatus) #endif #ifdef Q2SERVER -void SVC_InfoQ2 (void) +static void SVC_InfoQ2 (void) { char string[64]; int i, count; @@ -1301,7 +1318,7 @@ SV_CheckLog =================== */ #define LOG_FLUSH 10*60 -void SV_CheckLog (void) +static void SV_CheckLog (void) { sizebuf_t *sz; @@ -1332,7 +1349,7 @@ the same as the current sequence, an A2A_NACK will be returned instead of the data. ================ */ -void SVC_Log (void) +static void SVC_Log (void) { unsigned int seq; char data[MAX_DATAGRAM+64]; @@ -1439,7 +1456,7 @@ flood the server with invalid connection IPs. With a challenge, they must give a valid IP address. ================= */ -void SVC_GetChallenge (qboolean nodpresponse) +qboolean SVC_GetChallenge (qboolean respond_dp) { #ifdef HUFFNETWORK int compressioncrc; @@ -1449,24 +1466,71 @@ void SVC_GetChallenge (qboolean nodpresponse) int lng; char *over; - if (!sv_listen_qw.value && !sv_listen_dp.value && !sv_listen_q3.ival) - return; + qboolean respond_std = true; +#ifdef QWOVERQ3 + qboolean respond_qwoverq3 = true; + respond_qwoverq3 &= !!sv_listen_q3.value; +#else + const qboolean respond_qwoverq3 = false; +#endif + respond_std &= !!sv_listen_qw.value; + respond_dp &= !!sv_listen_dp.value; + + if (progstype == PROG_H2) + respond_dp = false; //don't bother. dp doesn't support the maps anyway. + //dp's connections result in race conditions or are ambiguous in certain regards + //race: dp vs nq. + // the dp request will generally arrive first. we check if there was a recent challenge requested, and inhibit the nq response, ensuring that dp clients connect with a known protocol + //race: dp vs qw. + // DP clients will just bindly respond to both with a connection request. sending the dp one usually means the server will see the dp connection request first + // FTE clients explicitly ignore dp challenges with the specific 'FTE' prefix so you get qw connections there. + //conflict: dp vs q2. dp challenge responses USUALLY contain letters. vanilla q2 is always a 32bit int. FTE clients will check that before sending an appropriate response. + //so: + // vanilla nq doesn't send getchallenge, its nq connect is not inhibited, and connects directly (we optionally hack a challenge over stuffcmds, as well as protocol extensions). + // dp gets a dp+qw challenge, its nq request is ignored due to packet ordering and a small timeout, the server sees the dp connection request first and ignores the qw connect. + // fte's nq request is treated as a getchallenge. fte clients ignore the dp challenge response (if qw protocols are still enabled). ends up with a qw/fte connection + if (!(sv_listen_nq.value || sv_bigcoords.value || !respond_std)) + respond_dp = false; + +#ifdef QWOVERQ3 + if (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM) + respond_qwoverq3 = false; //should probably just nuke this feature. +#endif + + if (!respond_std && !respond_dp && !respond_qwoverq3) + return false; + + if (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM) + { //if we're running q2 or q3, just ignore the whole DP thing. its irrelevent in those game modes. + respond_std |= true; +#ifdef QWOVERQ3 + respond_qwoverq3 = false; +#endif + respond_dp = false; + } + if (respond_dp) + respond_std = false; challenge = SV_NewChallenge(); - // send it back + //different game modes require different types of responses + switch(svs.gametype) + { #ifdef Q3SERVER - if (svs.gametype == GT_QUAKE3) //q3 servers + case GT_QUAKE3: //q3 servers buf = va("challengeResponse %i", challenge); - else + break; #endif #ifdef Q2SERVER - if (svs.gametype == GT_QUAKE2) + case GT_QUAKE2: buf = va("challenge %i", challenge); //quake 2 servers give a different challenge response - else + break; #endif + default: buf = va("%c%i", S2C_CHALLENGE, challenge); //quakeworld's response is a bit poo. + break; + } over = buf + strlen(buf) + 1; @@ -1527,12 +1591,25 @@ void SVC_GetChallenge (qboolean nodpresponse) over+=sizeof(lng); } #endif + +#ifdef HAVE_DTLS + if (sv_listen_dtls.ival/* || !*sv_listen_dtls.string*/) + { + lng = LittleLong(PROTOCOL_VERSION_DTLSUPGRADE); + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); + + if (sv_listen_dtls.ival >= 2) + lng = LittleLong(2); //required + else + lng = LittleLong(1); //supported + memcpy(over, &lng, sizeof(lng)); + over+=sizeof(lng); + } +#endif } - if (progstype == PROG_H2) - nodpresponse = true; - - if (!nodpresponse && sv_listen_dp.value && (sv_listen_nq.value || sv_bigcoords.value || !sv_listen_qw.value)) + if (respond_dp) { char *dp; if (sv_listen_qw.value) @@ -1542,22 +1619,23 @@ void SVC_GetChallenge (qboolean nodpresponse) Netchan_OutOfBand(NS_SERVER, &net_from, strlen(dp)+1, dp); } - if (sv_listen_qw.value || (svs.gametype != GT_PROGS && svs.gametype != GT_Q1QVM)) + if (respond_std) Netchan_OutOfBand(NS_SERVER, &net_from, over-buf, buf); -#ifdef Q3SERVER +#ifdef QWOVERQ3 if (svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM) { - if (sv_listen_q3.ival) + if (respond_qwoverq3) { buf = va("challengeResponse %i", challenge); Netchan_OutOfBand(NS_SERVER, &net_from, strlen(buf), buf); } } #endif + return true; } -void VARGS SV_OutOfBandPrintf (int q2, netadr_t *adr, char *format, ...) +static void VARGS SV_OutOfBandPrintf (int q2, netadr_t *adr, char *format, ...) { va_list argptr; char string[8192]; @@ -1579,7 +1657,7 @@ void VARGS SV_OutOfBandPrintf (int q2, netadr_t *adr, char *format, ...) Netchan_OutOfBand (NS_SERVER, adr, strlen(string), (qbyte *)string); } -void VARGS SV_OutOfBandTPrintf (int q2, netadr_t *adr, int language, translation_t text, ...) +static void VARGS SV_OutOfBandTPrintf (int q2, netadr_t *adr, int language, translation_t text, ...) { va_list argptr; char string[8192]; @@ -1621,7 +1699,7 @@ qboolean SV_ChallengePasses(int challenge) //this means that DP clients tend to connect as generic NQ clients. //and because DP _REQUIRES_ sv_bigcoords, they tend to end up being given fitz/rmq protocols //thus we don't respond to the connect if sv_listen_dp is 1, and we had a recent getchallenge request. recent is 2 secs. -qboolean SV_ChallengeRecent(void) +static qboolean SV_ChallengeRecent(void) { int curtime = realtime; //yeah, evil. sue me. consitent with challenges. int i; @@ -1872,7 +1950,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) if (client->frameunion.frames) Z_Free(client->frameunion.frames); - if ((client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))// || ISDPCLIENT(&temp)) + if ((client->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || ISDPCLIENT(client)) { char *ptr; int maxents = maxpacketentities*4; /*this is the max number of ents updated per frame. we can't track more, so...*/ @@ -2144,11 +2222,15 @@ client_t *SVC_DirectConnect(void) if (*Cmd_Argv(1) == '\\') { //connect "\key\val" - +#ifndef QWOVERQ3 + SV_RejectMessage (SCP_QUAKE3, "This is not a q3 server: %s\n", version_string()); + Con_TPrintf ("* rejected connect from q3 client\n"); + return NULL; +#else //this is used by q3 (note, we already decrypted the huffman connection packet in a hack) if (!sv_listen_q3.ival) { - SV_RejectMessage (SCP_QUAKE3, "Server is not accepting quake3 clients at this time.\n", version_string()); + SV_RejectMessage (SCP_QUAKE3, "Server is not accepting quake3 clients at this time: %s\n", version_string()); Con_TPrintf ("* rejected connect from q3 client\n"); return NULL; } @@ -2181,6 +2263,7 @@ client_t *SVC_DirectConnect(void) #ifdef HUFFNETWORK huffcrc = HUFFCRC_QUAKE3; +#endif #endif } else if (*(Cmd_Argv(0)+7) == '\\') @@ -2237,7 +2320,7 @@ client_t *SVC_DirectConnect(void) {"NEHAHRABJP", 0}, {"NEHAHRABJP2", 0}, {"NEHAHRABJP3", 1u< 2 && (net_from.prot == NP_DGRAM || net_from.prot == NP_STREAM || net_from.prot == NP_WS)) + { + SV_RejectMessage (protocol, "This server requires the use of DTLS/TLS/WSS.\n"); + return NULL; + } +#endif + { char *banreason = SV_BannedReason(&net_from); if (banreason) @@ -2509,7 +2600,7 @@ client_t *SVC_DirectConnect(void) else if (!strcmp(sv_protocol_nq.string, "dp6")) protocol = SCP_DARKPLACES6; else if (!strcmp(sv_protocol_nq.string, "dp7")) - protocol = SCP_DARKPLACES6; + protocol = SCP_DARKPLACES7; else if (!strcmp(sv_protocol_nq.string, "id") || !strcmp(sv_protocol_nq.string, "vanilla")) protocol = SCP_NETQUAKE; else switch(sv_protocol_nq.ival) @@ -3451,6 +3542,11 @@ qboolean SV_ConnectionlessPacket (void) #endif else if (!strncmp(c,"connect", 7)) { +#ifdef HAVE_DTLS + if (net_from.prot == NP_DGRAM) + NET_DTLS_Disconnect(svs.sockets, &net_from); +#endif + #ifdef Q3SERVER if (svs.gametype == GT_QUAKE3) { @@ -3458,6 +3554,7 @@ qboolean SV_ConnectionlessPacket (void) return true; } +#ifdef QWOVERQ3 if (sv_listen_q3.ival) { if (!strstr(s, "\\name\\")) @@ -3473,7 +3570,9 @@ qboolean SV_ConnectionlessPacket (void) } } #endif - if (secure.value) //FIXME: possible problem for nq clients when enabled +#endif + + if (secure.value) //FIXME: possible problem for nq clients when enabled { Netchan_OutOfBandTPrintf (NS_SERVER, &net_from, svs.language, "%c\nThis server requires client validation.\nPlease use the "FULLENGINENAME" validation program\n", A2C_PRINT); } @@ -3483,13 +3582,44 @@ qboolean SV_ConnectionlessPacket (void) return true; } } - else if (!strcmp(c,"\xad\xad\xad\xad""getchallenge")) + else if (!strcmp(c,"dtlsconnect")) { - SVC_GetChallenge (true); +#ifdef HAVE_DTLS + if (net_from.prot == NP_DGRAM && (sv_listen_dtls.ival /*|| !*sv_listen_dtls.ival*/)) + { + if (SV_ChallengePasses(atoi(Cmd_Argv(1)))) + { + char *banreason = SV_BannedReason(&net_from); + if (banreason) + { + if (*banreason) + SV_RejectMessage (SCP_QUAKEWORLD, "You were banned.\nReason: %s\n", banreason); + else + SV_RejectMessage (SCP_QUAKEWORLD, "You were banned.\n"); + } + else + { + //NET_DTLS_Disconnect(svs.sockets, &net_from); + if (NET_DTLS_Create(svs.sockets, &net_from)) + Netchan_OutOfBandPrint(NS_SERVER, &net_from, "dtlsopened"); + } + } + else + SV_RejectMessage (SCP_QUAKEWORLD, "Bad challenge.\n"); + } + return true; +#endif } - else if (!strcmp(c,"getchallenge")) + /*else if (!strcmp(c,"\xad\xad\xad\xad""getchallenge")) { SVC_GetChallenge (false); + }*/ + else if (!strcmp(c,"getchallenge")) + { + //qw+q2 always sends "\xff\xff\xff\xffgetchallenge\n" + //dp+q3 always sends "\xff\xff\xff\xffgetchallenge" + //its a subtle difference, but means we can avoid wasteful spam for real qw clients. + SVC_GetChallenge ((net_message.cursize==16)?true:false); } #ifdef NQPROT /*for DP*/ @@ -3537,6 +3667,7 @@ qboolean SVNQ_ConnectionlessPacket(void) char buffer[256], buffer2[256]; netadr_t localaddr; char *banreason; + if (net_from.type == NA_LOOPBACK) return false; @@ -3689,7 +3820,7 @@ qboolean SVNQ_ConnectionlessPacket(void) else if (!strncmp(MSG_ReadString(), "getchallenge", 12) && (sv_listen_qw.ival || sv_listen_dp.ival)) { /*dual-stack client, supporting either DP or QW protocols*/ - SVC_GetChallenge (true); + SVC_GetChallenge (false); } else { @@ -4024,6 +4155,21 @@ qboolean SV_ReadPackets (float *delay) SV_ConnectionlessPacket(); continue; } +#ifdef HAVE_DTLS + else + { + if (NET_DTLS_Decode(svs.sockets)) + { + if (!net_message.cursize) + continue; + if (*(unsigned int *)net_message.data == ~0) + { + SV_ConnectionlessPacket(); + continue; + } + } + } +#endif #ifdef Q3SERVER if (svs.gametype == GT_QUAKE3) @@ -4139,7 +4285,7 @@ dominping: if (i != svs.allocated_client_slots) continue; -#ifdef Q3SERVER +#ifdef QWOVERQ3 if (sv_listen_q3.ival && SVQ3_HandleClient()) { received++; @@ -4162,6 +4308,10 @@ dominping: Con_Printf ("%s:sequenced packet without connection\n", NET_AdrToString (com_token, sizeof(com_token), &net_from)); //hack: com_token cos we need some random temp buffer. } +#ifdef HAVE_DTLS + NET_DTLS_Timeouts(svs.sockets); +#endif + return received; } @@ -4848,8 +4998,13 @@ void SV_InitLocal (void) Cvar_Register (&sv_listen_qw, cvargroup_servercontrol); Cvar_Register (&sv_listen_nq, cvargroup_servercontrol); Cvar_Register (&sv_listen_dp, cvargroup_servercontrol); +#ifdef QWOVERQ3 Cvar_Register (&sv_listen_q3, cvargroup_servercontrol); - sv_listen_qw.restriction = RESTRICT_MAX; +#endif +#ifdef HAVE_DTLS + Cvar_Register (&sv_listen_dtls, cvargroup_servercontrol); +#endif + sv_listen_qw.restriction = RESTRICT_MAX; //no disabling this over rcon. Cvar_Register (&fraglog_public, cvargroup_servercontrol); SVNET_RegisterCvars(); diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index ec09a69e..10f31b9b 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -2389,7 +2389,11 @@ void SV_UserCmdMVDList_HTML (vfsfile_t *pipe) VFS_PRINTF(pipe, "*%d: %s %dk
\n", i, list->name, d->totalsize/1024); } if (!d) - VFS_PRINTF(pipe, "%d: %s %dk play
\n", i, list->name, list->name, list->size/1024, list->name); + { + char datetime[64]; + strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", localtime(&list->mtime)); + VFS_PRINTF(pipe, "%d: %s %dk play %s
\n", i, list->name, list->name, list->size/1024, list->name, datetime); + } } for (d = demo.dest; d; d = d->nextdest) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index b2170602..31b151e5 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -528,8 +528,8 @@ void SVNQ_New_f (void) qboolean big; //used as a filter to exclude protocols that don't match our coord+angles mode } preferedprot[] = { - {SCP_DARKPLACES7, true}, - {SCP_DARKPLACES6, true}, +// {SCP_DARKPLACES7, true}, +// {SCP_DARKPLACES6, true}, {SCP_FITZ666, true}, //actually 999... shh... {SCP_FITZ666, false}, {SCP_BJP3, false} @@ -604,7 +604,7 @@ void SVNQ_New_f (void) protoname = "NQ"; } break; - case SCP_DARKPLACES6: + /*case SCP_DARKPLACES6: SV_LogPlayer(host_client, "new (DP6)"); protmain = PROTOCOL_VERSION_DP6; protext1 &= ~PEXT_FLOATCOORDS; //always enabled, try not to break things @@ -615,7 +615,7 @@ void SVNQ_New_f (void) protmain = PROTOCOL_VERSION_DP7; protext1 &= ~PEXT_FLOATCOORDS; //always enabled, try not to break things protoname = "DPP7"; - break; + break;*/ default: host_client->drop = true; protoname = "?""?""?"; @@ -6869,7 +6869,7 @@ void SV_RunCmd (usercmd_t *ucmd, qboolean recurse) VectorCopy(sv_player->v->origin, pmove.origin); VectorCopy(sv_player->v->oldorigin, pmove.safeorigin); - pmove.safeorigin_known = true; + pmove.safeorigin_known = progstype != PROG_QW; VectorCopy (sv_player->v->velocity, pmove.velocity); VectorCopy (sv_player->v->v_angle, pmove.angles); diff --git a/engine/server/svq3_game.c b/engine/server/svq3_game.c index 483d0f1f..4387e730 100644 --- a/engine/server/svq3_game.c +++ b/engine/server/svq3_game.c @@ -78,7 +78,9 @@ static qboolean BoundsIntersect (vec3_t mins1, vec3_t maxs1, vec3_t mins2, vec3_ void SVQ3_CreateBaseline(void); void SVQ3_ClientThink(client_t *cl); -void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3); +#ifdef QWOVERQ3 +static void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3); +#endif const char *mapentspointer; @@ -1991,9 +1993,8 @@ void SVQ3_EmitPacketEntities(client_t *client, q3client_frame_t *from, q3client_ if(newnum < oldnum) { // this is a new entity, send it from the baseline - if (svs.gametype == GT_QUAKE3) - MSGQ3_WriteDeltaEntity( msg, &q3_baselines[newnum], newent, true ); - else +#ifdef QWOVERQ3 + if (svs.gametype != GT_QUAKE3) { q3entityState_t q3base; edict_t *e; @@ -2001,6 +2002,9 @@ void SVQ3_EmitPacketEntities(client_t *client, q3client_frame_t *from, q3client_ SVQ3Q1_ConvertEntStateQ1ToQ3(&e->baseline, &q3base); MSGQ3_WriteDeltaEntity( msg, &q3base, newent, true ); } + else +#endif + MSGQ3_WriteDeltaEntity( msg, &q3_baselines[newnum], newent, true ); newindex++; continue; } @@ -2230,7 +2234,7 @@ static qboolean SVQ3_EntityIsVisible(q3client_frame_t *snap, q3sharedEntity_t *e return true; } -q3playerState_t *SVQ3Q1_BuildPlayerState(client_t *client) +static q3playerState_t *SVQ3Q1_BuildPlayerState(client_t *client) { static q3playerState_t state; extern cvar_t sv_gravity; @@ -2411,6 +2415,7 @@ void SVQ3_BuildClientSnapshot( client_t *client ) } } } +#ifdef QWOVERQ3 else { //our q1->q3 converter packet_entities_t pack; @@ -2426,6 +2431,7 @@ void SVQ3_BuildClientSnapshot( client_t *client ) entityStates[snap->num_entities++] = &q3packentities[i]; } } +#endif if( q3_next_snapshot_entities + snap->num_entities >= 0x7FFFFFFE ) { @@ -2450,10 +2456,10 @@ void SVQ3_BuildClientSnapshot( client_t *client ) { //fix areabits, q2->q3 style.. snap->areabits[i]^=255; } - } -void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3) +#ifdef QWOVERQ3 +static void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3) { #ifdef warningmsg #pragma warningmsg("qwoverq3: This _WILL_ need extending") @@ -2511,31 +2517,9 @@ void SVQ3Q1_ConvertEntStateQ1ToQ3(entity_state_t *q1, q3entityState_t *q3) q3->angles2[2] = 0; q3->constantLight = q1->abslight; q3->frame = q1->frame; - -#if 0 - -//these are the things I've not packed in to the above structure yet. -#if defined(Q2CLIENT) || defined(Q2SERVER) - int renderfx; //q2 - qbyte modelindex3; //q2 - qbyte modelindex4; //q2 -#endif - qbyte glowsize; - qbyte glowcolour; - qbyte scale; - char fatness; - qbyte hexen2flags; - qbyte dpflags; - qbyte colormod[3];//multiply this by 8 to read as 0 to 1... - qbyte lightstyle; - qbyte lightpflags; - unsigned short light[4]; - unsigned short tagentity; - unsigned short tagindex; -#endif } -void SVQ3Q1_SendGamestateConfigstrings(sizebuf_t *msg) +static void SVQ3Q1_SendGamestateConfigstrings(sizebuf_t *msg) { const int cs_models = 32; const int cs_sounds = cs_models + 256; @@ -2614,6 +2598,7 @@ void SVQ3Q1_SendGamestateConfigstrings(sizebuf_t *msg) MSG_WriteBits(msg, 0, 8); } } +#endif //writes initial gamestate void SVQ3_SendGameState(client_t *client) @@ -2664,6 +2649,7 @@ void SVQ3_SendGameState(client_t *client) MSGQ3_WriteDeltaEntity( &msg, NULL, &q3_baselines[i], true ); } break; +#ifdef QWOVERQ3 case GT_PROGS: case GT_Q1QVM: SVQ3Q1_SendGamestateConfigstrings(&msg); @@ -2680,10 +2666,9 @@ void SVQ3_SendGameState(client_t *client) } } break; - // warning: enumeration value GT_? not handled in switch - case GT_HALFLIFE: - case GT_QUAKE2: - case GT_MAX: +#endif + default: + client->drop = true; break; } diff --git a/engine/shaders/generatebuiltinsl.c b/engine/shaders/generatebuiltinsl.c index 92701828..227aab2b 100644 --- a/engine/shaders/generatebuiltinsl.c +++ b/engine/shaders/generatebuiltinsl.c @@ -25,6 +25,7 @@ char shaders[][64] = "defaultgammacb", "drawflat_wall", "wireframe", + "itemtimer", "lpp_depthnorm", "lpp_light", "lpp_wall", diff --git a/engine/shaders/glsl/itemtimer.glsl b/engine/shaders/glsl/itemtimer.glsl new file mode 100644 index 00000000..56c0e777 --- /dev/null +++ b/engine/shaders/glsl/itemtimer.glsl @@ -0,0 +1,43 @@ +!!permu FOG + +#include "sys/defs.h" +#include "sys/fog.h" + +varying vec2 tc; +varying vec4 vc; + +#ifdef VERTEX_SHADER +void main () +{ + tc = v_texcoord; + vc = v_colour; + gl_Position = ftetransform(); +} +#endif + + +#ifdef FRAGMENT_SHADER +void main () +{ + gl_FragColor = vec4(0.5,0.5,0.5,1);//texture2D(s_diffuse, tc.xy); + + vec2 st = (tc-floor(tc)) - 0.5; + st *= 2.0; + float dist = sqrt(dot(st,st)); + + float ring = 1.0 + smoothstep(0.9, 1.0, dist) + - smoothstep(0.8, 0.9, dist); + + //fade out the rim + if ((atan(st.t, st.s)+3.14)/6.28 > vc.a) + gl_FragColor.a *= 0.25; + gl_FragColor.rgb *= mix(vc.rgb, vec3(0.0), ring); +//gl_FragColor.a; + +//and finally hide it all if we're fogged. +#ifdef FOG + gl_FragColor = fog4additive(gl_FragColor); +#endif +} +#endif + diff --git a/engine/sw/sw.h b/engine/sw/sw.h index 9724fb1c..09308450 100644 --- a/engine/sw/sw.h +++ b/engine/sw/sw.h @@ -149,7 +149,7 @@ void SWRast_Sync(struct workqueue_s *wq); qboolean SW_VID_Init(rendererstate_t *info, unsigned char *palette); void SW_VID_DeInit(void); qboolean SW_VID_ApplyGammaRamps (unsigned int rampcount, unsigned short *ramps); -char *SW_VID_GetRGBInfo(int *truevidwidth, int *truevidheight, enum uploadfmt *fmt); +char *SW_VID_GetRGBInfo(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt); void SW_VID_SetWindowCaption(const char *msg); void SW_VID_SwapBuffers(void); void SW_VID_UpdateViewport(wqcom_t *com); diff --git a/engine/sw/sw_backend.c b/engine/sw/sw_backend.c index 7656e4a2..89b5ee11 100644 --- a/engine/sw/sw_backend.c +++ b/engine/sw/sw_backend.c @@ -575,6 +575,12 @@ void SWBE_Set2D(void) float ang, rad, w, h; float tmp[16]; float tmp2[16]; + + vid.fbvwidth = vid.width; + vid.fbvheight = vid.height; + vid.fbpwidth = vid.pixelwidth; + vid.fbpheight = vid.pixelheight; + ang = (gl_screenangle.value>0?(gl_screenangle.value+45):(gl_screenangle.value-45))/90; ang = (int)ang * 90; if (ang) diff --git a/engine/sw/sw_rast.c b/engine/sw/sw_rast.c index df2f649d..4c02c2b1 100644 --- a/engine/sw/sw_rast.c +++ b/engine/sw/sw_rast.c @@ -906,7 +906,26 @@ void SW_R_RenderView(void) if (!cl.worldmodel || (!cl.worldmodel->nodes && cl.worldmodel->type != mod_heightmap)) r_refdef.flags |= RDF_NOWORLDMODEL; -// R_SetupGL (); + //no fbos here + vid.fbvwidth = vid.width; + vid.fbvheight = vid.height; + vid.fbpwidth = vid.pixelwidth; + vid.fbpheight = vid.pixelheight; + + { + //figure out the viewport that we should be using. + int x = floor(r_refdef.vrect.x * (float)vid.fbpwidth/(float)vid.width); + int x2 = ceil((r_refdef.vrect.x + r_refdef.vrect.width) * (float)vid.fbpwidth/(float)vid.width); + int y = floor(r_refdef.vrect.y * (float)vid.fbpheight/(float)vid.height); + int y2 = ceil((r_refdef.vrect.y + r_refdef.vrect.height) * (float)vid.fbpheight/(float)vid.height); + int w = x2 - x; + int h = y2 - y; + r_refdef.pxrect.x = x; + r_refdef.pxrect.y = y; + r_refdef.pxrect.width = w; + r_refdef.pxrect.height = h; + r_refdef.pxrect.maxheight = vid.fbpheight; + } AngleVectors (r_refdef.viewangles, vpn, vright, vup); VectorCopy (r_refdef.vieworg, r_origin); @@ -982,7 +1001,6 @@ qboolean SW_SCR_UpdateScreen(void) if (!CSQC_DrawView()) V_RenderView (); - R2D_PolyBlend (); R2D_BrightenScreen(); } diff --git a/engine/sw/sw_vidwin.c b/engine/sw/sw_vidwin.c index 0803ab3f..4e356193 100644 --- a/engine/sw/sw_vidwin.c +++ b/engine/sw/sw_vidwin.c @@ -774,7 +774,7 @@ qboolean SW_VID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ra { return false; } -char *SW_VID_GetRGBInfo(int *truevidwidth, int *truevidheight, enum uploadfmt *fmt) +char *SW_VID_GetRGBInfo(int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt) { char *buf = NULL; char *src, *dst; @@ -790,6 +790,7 @@ char *SW_VID_GetRGBInfo(int *truevidwidth, int *truevidheight, enum uploadfmt *f dst[2] = src[0]; } } + *bytestride = vid.pixelwidth*3; *truevidwidth = vid.pixelwidth; *truevidheight = vid.pixelheight; *fmt = TF_BGR24; diff --git a/engine/web/ftejslib.h b/engine/web/ftejslib.h index 84ef49a6..e4918ff6 100644 --- a/engine/web/ftejslib.h +++ b/engine/web/ftejslib.h @@ -53,8 +53,8 @@ int emscriptenfte_setupcanvas( void(*Button)(unsigned int devid, int down, int mbutton), int(*Keyboard)(unsigned int devid, int down, int keycode, int unicode), void(*LoadFile)(char *url, char *mime, int filehandle), - void(*buttonevent)(unsigned int joydev, int button, int ispressed), - void(*axisevent)(unsigned int joydev, int axis, float value), + void(*buttonevent)(unsigned int joydev, int button, int ispressed, int isstandard), + void(*axisevent)(unsigned int joydev, int axis, float value, int isstandard), int (*ShouldSwitchToFullscreen)(void) ); diff --git a/engine/web/ftejslib.js b/engine/web/ftejslib.js index 69ec16e5..a09747f2 100644 --- a/engine/web/ftejslib.js +++ b/engine/web/ftejslib.js @@ -266,10 +266,10 @@ mergeInto(LibraryManager.library, delete FTEH.gamepads[gp.index]; if (FTEC.evcb.jaxis) //try and clear out the axis when released. for (var j = 0; j < 6; j+=1) - Runtime.dynCall('viid', FTEC.evcb.jaxis, [gp.index, j, 0]); + Runtime.dynCall('viidi', FTEC.evcb.jaxis, [gp.index, j, 0, true]); if (FTEC.evcb.jbutton) //try and clear out the axis when released. for (var j = 0; j < 32+4; j+=1) - Runtime.dynCall('viid', FTEC.evcb.jbutton, [gp.index, j, 0]); + Runtime.dynCall('viiii', FTEC.evcb.jbutton, [gp.index, j, 0, true]); console.log("Gamepad disconnected from index %d: %s", gp.index, gp.id); break; case 'pointerlockchange': @@ -362,9 +362,9 @@ mergeInto(LibraryManager.library, //with events, we can do unplug stuff properly. //otherwise hot unplug might be buggy. var gamepads; - if (FTEH.gamepads !== undefined) - gamepads = FTEH.gamepads; - else +// if (FTEH.gamepads !== undefined) +// gamepads = FTEH.gamepads; +// else gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads : []); if (gamepads !== undefined) @@ -380,23 +380,18 @@ mergeInto(LibraryManager.library, var b = gp.buttons[j]; var p; if (typeof(b) == "object") - { - p = b.pressed; - if (b.lastframe != p) - { //cache it to avoid spam - b.lastframe = p; - Runtime.dynCall('viii', FTEC.evcb.jbutton, [gp.index, j, p]); - } - } + p = b.pressed; //.value is a fractional thing. oh well. else - {//old chrome bug - p = b==1.0; - //warning: no cache. this is going to be spammy. - Runtime.dynCall('viii', FTEC.evcb.jbutton, [gp.index, j, p]); + p = b > 0.5; //old chrome bug + + if (b.lastframe != p) + { //cache it to avoid spam + b.lastframe = p; + Runtime.dynCall('viiii', FTEC.evcb.jbutton, [gp.index, j, p, gp.mapping=="standard"]); } } for (var j = 0; j < gp.axes.length; j+=1) - Runtime.dynCall('viid', FTEC.evcb.jaxis, [gp.index, j, gp.axes[j]]); + Runtime.dynCall('viidi', FTEC.evcb.jaxis, [gp.index, j, gp.axes[j], gp.mapping=="standard"]); } }, emscriptenfte_setupcanvas__deps: ['$FTEC', '$Browser', 'emscriptenfte_buf_createfromarraybuf'], @@ -437,7 +432,6 @@ mergeInto(LibraryManager.library, 'keypress', 'keydown', 'keyup', 'touchstart', 'touchend', 'touchcancel', 'touchleave', 'touchmove', 'dragenter', 'dragover', 'drop', - 'gamepadconnected', 'gamepaddisconnected', 'message', 'pointerlockchange', 'mozpointerlockchange', 'webkitpointerlockchange', 'focus', 'blur']; //try to fix alt-tab @@ -453,7 +447,7 @@ mergeInto(LibraryManager.library, document.addEventListener(event, FTEC.handleevent, true); }); - var windowevents = ['message','vrdisplaypresentchange','vrdisplayactivate','vrdisplaydeactivate']; + var windowevents = ['message','vrdisplaypresentchange','vrdisplayactivate','vrdisplaydeactivate','gamepadconnected', 'gamepaddisconnected']; windowevents.forEach(function(event) { window.addEventListener(event, FTEC.handleevent, true); diff --git a/engine/web/gl_vidweb.c b/engine/web/gl_vidweb.c index ffd9b53f..f1d33fc5 100644 --- a/engine/web/gl_vidweb.c +++ b/engine/web/gl_vidweb.c @@ -11,17 +11,79 @@ extern qboolean vid_isfullscreen; qboolean mouseactive; extern qboolean mouseusedforgui; +static int gamepaddeviceids[] = {DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET,DEVID_UNSET}; +static int keyboardid[] = {0}; +static int mouseid[] = {0}; static void *GLVID_getsdlglfunction(char *functionname) { return NULL; } -static void IN_JoystickButtonEvent(unsigned int joydevid, int button, int ispressed) +static void IN_GamePadButtonEvent(unsigned int joydevid, int button, int ispressed, int isstandardmapping) { - if (button >= 32+4) - return; - IN_KeyEvent(joydevid, ispressed, K_JOY1+button, 0); + int standardmapping[] = + { //the order of these keys is different from that of xinput + //however, the quake button codes should be the same. I really ought to define some K_ aliases for them. + K_GP_A, + K_GP_B, + K_GP_X, + K_GP_Y, + K_GP_LEFT_SHOULDER, + K_GP_RIGHT_SHOULDER, + K_GP_LEFT_TRIGGER, + K_GP_RIGHT_TRIGGER, + K_GP_BACK, + K_GP_START, + K_GP_LEFT_THUMB, + K_GP_RIGHT_THUMB, + K_GP_DPAD_UP, + K_GP_DPAD_DOWN, + K_GP_DPAD_LEFT, + K_GP_DPAD_RIGHT, + K_GP_GUIDE, + //K_GP_UNKNOWN + }; + + if (joydevid < countof(gamepaddeviceids)) + { + if (joydevid == gamepaddeviceids[joydevid]) + { + if (!ispressed) + return; //don't send axis events until its enabled. + gamepaddeviceids[joydevid] = joydevid; + } + joydevid = gamepaddeviceids[joydevid]; + } + + if (isstandardmapping) + { + if (button < countof(standardmapping)) + IN_KeyEvent(joydevid, ispressed, standardmapping[button], 0); + } + else + { + if (button < 32+4) + IN_KeyEvent(joydevid, ispressed, K_JOY1+button, 0); + } +} + +static void IN_GamePadAxisEvent(unsigned int joydevid, int axis, float value, int isstandardmapping) +{ + if (joydevid < countof(gamepaddeviceids)) + { + joydevid = gamepaddeviceids[joydevid]; + if (joydevid == DEVID_UNSET) + return; //don't send axis events until its enabled. + } + if (isstandardmapping) + { + int axismap[] = {GPAXIS_LT_RIGHT,GPAXIS_LT_DOWN,GPAXIS_RT_RIGHT,GPAXIS_RT_DOWN}; + if (axis < countof(axismap)) + IN_JoystickAxisEvent(joydevid, axismap[axis], value); + } + else + IN_JoystickAxisEvent(joydevid, axis, value); } static void VID_Resized(int width, int height) @@ -119,7 +181,7 @@ static int DOM_KeyEvent(unsigned int devid, int down, int scan, int uni) scan = domkeytoquake(scan); uni = (scan >= 32 && scan <= 127)?scan:0; } - IN_KeyEvent(devid, down, scan, uni); + IN_KeyEvent(keyboardid[devid], down, scan, uni); //Chars which don't map to some printable ascii value get preventDefaulted. //This is to stop fucking annoying fucking things like backspace randomly destroying the page and thus game. //And it has to be conditional, or we don't get any unicode chars at all. @@ -137,12 +199,12 @@ static void DOM_ButtonEvent(unsigned int devid, int down, int button) //fixme: the event is a float. we ignore that. while(button < 0) { - IN_KeyEvent(devid, true, K_MWHEELUP, 0); + IN_KeyEvent(mouseid[devid], true, K_MWHEELUP, 0); button += 1; } while(button > 0) { - IN_KeyEvent(devid, true, K_MWHEELDOWN, 0); + IN_KeyEvent(mouseid[devid], true, K_MWHEELDOWN, 0); button -= 1; } } @@ -154,9 +216,13 @@ static void DOM_ButtonEvent(unsigned int devid, int down, int button) else if (button == 1) button = 2; - IN_KeyEvent(devid, down, K_MOUSE1+button, 0); + IN_KeyEvent(mouseid[devid], down, K_MOUSE1+button, 0); } } +void DOM_MouseMove(unsigned int devid, int abs, float x, float y, float z, float size) +{ + IN_MouseMove(mouseid[devid], abs, x, y, z, size); +} void DOM_LoadFile(char *loc, char *mime, int handle) { @@ -206,12 +272,12 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) info->width, info->height, VID_Resized, - IN_MouseMove, + DOM_MouseMove, DOM_ButtonEvent, DOM_KeyEvent, DOM_LoadFile, - IN_JoystickButtonEvent, - IN_JoystickAxisEvent, + IN_GamePadButtonEvent, + IN_GamePadAxisEvent, VID_ShouldSwitchToFullscreen )) { @@ -307,5 +373,22 @@ void INS_Commands (void) } void INS_EnumerateDevices(void *ctx, void(*callback)(void *ctx, const char *type, const char *devicename, unsigned int *qdevid)) { + size_t i; + char foobar[64]; + for (i = 0; i < countof(gamepaddeviceids); i++) + { + Q_snprintfz(foobar, sizeof(foobar), "gp%i", i); + callback(ctx, "gamepad", foobar, &gamepaddeviceids[i]); + } + for (i = 0; i < countof(mouseid); i++) + { + Q_snprintfz(foobar, sizeof(foobar), "m%i", i); + callback(ctx, "mouse", foobar, &mouseid[i]); + } + for (i = 0; i < countof(keyboardid); i++) + { + Q_snprintfz(foobar, sizeof(foobar), "kb%i", i); + callback(ctx, "keyboard", foobar, &keyboardid[i]); + } } diff --git a/specs/browser.txt b/specs/browser.txt index 6a1e5ecb..cf772644 100644 --- a/specs/browser.txt +++ b/specs/browser.txt @@ -1,6 +1,3 @@ -FIXME: verify+clarify these docs... - - There are multiple ways to embed a program into a browser. The 'web'/emscripten port, the nacl port, the npapi port, and the activex port. Quick start with browser-servers: @@ -31,7 +28,7 @@ sv_port_rtc - This says the broker+resource used to register a webrtc server. cfg_save - This command saves your config to your browser's local storage. In combination with seta, you can save most settings this way. Beware that browsers might still wipe it all eventually. -Hosting: +Hosting Quake: To get fte running on a web page, you will need: ftewebgl.html - An html file that embeds the javascript. You can probably modify fte's default if you want to integrate it better with your site, it doesn't change much. ftewebgl.js - This is the meat of the engine. All in a single file. pre-gzip it if you can, to keep sizes down. @@ -48,11 +45,14 @@ The browser port is set up to ignore most args when linked to from another site. Built-in http server: The http server provided by sv_port_tcp will provide a page (either directly or with a redirect) to a version of the webgl client. Thanks to allow_download_* cvars, only certain things may be downloaded. +Unlike over game connections, the server will transparently also provide http content compression only where a .gz file has also been provided (and is more recent and in the same gamedir). + Additionally, there are also some files generated by the server. index.html (and no resource) - attempts to provide a link to the webgl version of fte. -default.fmf - provides a redirect to the manifest's update url. all other files match what you would be able to download over udp. + + Manifest files: These are FTE's way of reconfiguring FTE for standalone mods. They offer basic rebranding features as well as content updates. They contain a number of attributes, and frankly its easier to start with an example. Check http://triptohell.info/moodles/web/ for a few.