From 82542ae03723d1f8e0eb775326968a3fbcbfcd5a Mon Sep 17 00:00:00 2001 From: Spoike Date: Mon, 9 Apr 2012 19:12:12 +0000 Subject: [PATCH] Committing this before I break it any more. Massive terrain system rewrite. Added a Native Client port (sound is stereo 44khz only, rendering is gles2, networking is websockets only (sv_port_tcp supports acting as a qw websockets server with non-nacl servers, filesystem is downloads-only - no saves/configs). Blame Zalon. Grr. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4013 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/Makefile | 24 +- engine/client/cl_master.h | 2 +- engine/client/cl_ui.c | 15 +- engine/client/console.c | 2 +- engine/client/in_generic.c | 365 +++++ engine/client/keys.c | 10 +- engine/client/m_master.c | 4 +- engine/client/m_mp3.c | 2 +- engine/client/menu.c | 2 + engine/client/net_master.c | 2 +- engine/client/pr_csqc.c | 18 + engine/client/pr_menu.c | 14 +- engine/client/r_surf.c | 15 +- engine/client/render.h | 4 +- engine/client/renderer.c | 70 +- engine/client/snd_al.c | 2 +- engine/client/snd_alsa.c | 2 +- engine/client/snd_directx.c | 2 +- engine/client/snd_dma.c | 6 +- engine/client/snd_droid.c | 4 +- engine/client/snd_linux.c | 2 +- engine/client/snd_macos.c | 2 +- engine/client/snd_mix.c | 6 +- engine/client/snd_morphos.c | 2 +- engine/client/snd_sdl.c | 2 +- engine/client/snd_win.c | 2 +- engine/client/sound.h | 2 +- engine/client/sys_droid.c | 28 +- engine/common/bothdefs.h | 21 +- engine/common/common.c | 1 - engine/common/common.h | 1 + engine/common/fs.c | 10 +- engine/common/fs_stdio.c | 3 + engine/common/net.h | 9 +- engine/common/net_wins.c | 987 +++++++++++- engine/common/netinc.h | 32 +- engine/common/plugin.c | 14 + engine/common/pr_bgcmd.c | 31 +- engine/common/protocol.h | 1 + engine/common/sha1.c | 190 +++ engine/common/world.h | 2 +- engine/dotnet2005/ftequake.sln | 38 + engine/dotnet2005/ftequake.vcproj | 382 ++--- .../droid/src/com/fteqw/FTEDroidActivity.java | 12 +- .../droid/src/com/fteqw/FTEDroidEngine.java | 2 +- engine/gl/gl_alias.c | 12 +- engine/gl/gl_backend.c | 41 +- engine/gl/gl_draw.c | 34 +- engine/gl/gl_heightmap.c | 1380 ++++++++++++----- engine/gl/gl_model.c | 6 + engine/gl/gl_ngraph.c | 1 + engine/gl/gl_rmain.c | 2 +- engine/gl/gl_rmisc.c | 6 +- engine/gl/gl_screen.c | 1 + engine/gl/gl_shader.c | 54 +- engine/gl/gl_vidcommon.c | 101 +- engine/gl/glquake.h | 34 +- engine/gl/shader.h | 2 + engine/http/ftpclient.c | 10 +- engine/http/ftpserver.c | 4 +- engine/http/httpserver.c | 2 +- engine/nacl/fs_ppapi.c | 858 ++++++++++ engine/nacl/gl_vidppapi.c | 277 ++++ engine/nacl/snd_ppapi.c | 115 ++ engine/nacl/sys_ppapi.c | 614 ++++++++ engine/server/pr_cmds.c | 53 +- engine/server/sv_main.c | 12 +- engine/server/sv_mvd.c | 22 +- engine/server/sv_user.c | 368 ++--- engine/server/svmodel.c | 9 + 70 files changed, 5297 insertions(+), 1068 deletions(-) create mode 100644 engine/client/in_generic.c create mode 100644 engine/common/sha1.c create mode 100644 engine/nacl/fs_ppapi.c create mode 100644 engine/nacl/gl_vidppapi.c create mode 100644 engine/nacl/snd_ppapi.c create mode 100644 engine/nacl/sys_ppapi.c diff --git a/engine/Makefile b/engine/Makefile index 813d79b60..0513f83bb 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -246,6 +246,7 @@ HTTP_DIR=$(BASE_DIR)/http LIBS_DIR?=. PROGS_DIR=$(BASE_DIR)/qclib SNDCODEC_DIR=$(BASE_DIR)/sndcodec +NACL_DIR=$(BASE_DIR)/nacl BOTLIB_DIR=$(BASE_DIR)/botlib RELEASE_DIR=$(BASE_DIR)/release @@ -589,6 +590,27 @@ endif #specific targets override those defaults as needed. +#google native client +ifeq ($(FTE_TARGET),nacl) + ifeq ($(shell uname -o 2>&1 | grep Cygwin),) + CC=$(NACL_SDK_ROOT)/toolchain/linux_x86_newlib/bin/i686-nacl-gcc -DNACL -m$(BITS) + STRIP=$(NACL_SDK_ROOT)/toolchain/linux_x86_newlib/bin/i686-nacl-strip + else + CC=$(NACL_SDK_ROOT)/toolchain/win_x86_newlib/bin/i686-nacl-gcc -DNACL -m$(BITS) + STRIP=$(NACL_SDK_ROOT)/toolchain/win_x86_newlib/bin/i686-nacl-strip + endif + + BASELDFLAGS = -lm -lpthread -lppapi_gles2 -lnosys -lppapi + IMAGELDFLAGS = + + GLCL_OBJS=$(GL_OBJS) $(D3DGL_OBJS) $(GLQUAKE_OBJS) sys_ppapi.o cd_null.o gl_vidppapi.o in_generic.o fs_ppapi.o snd_ppapi.o + + GLB_DIR=gl_nacl_x86_$(BITS) + GL_EXE_NAME=../fteqw_x86_$(BITS).nexe + GLCL_EXE_NAME=../fteqwcl_x86_$(BITS).nexe + MINGL_DIR=mingl_nacl_x86_$(BITS) + MINGL_EXE_NAME=../fteqw_mingl_x86_$(BITS).nexe +endif #FTE_TARGET=win32_SDL | FTE_TARGET=win64_SDL (MinGW32 + SDL | MinGW64 + SDL) ifeq ($(shell echo $(FTE_TARGET)|grep -E -i -v "win(32|64)_sdl$$"),) @@ -948,7 +970,7 @@ ifneq ($(OUT_DIR),) endif -VPATH = $(BASE_DIR) : $(CLIENT_DIR) : $(GL_DIR) : $(COMMON_DIR) : $(SERVER_DIR) : $(HTTP_DIR) : $(BASE_DIR)/irc : $(BASE_DIR)/email : $(QUX_DIR) : $(PROGS_DIR) : $(SNDCODEC_DIR) : $(D3D_DIR) : $(BOTLIB_DIR) +VPATH = $(BASE_DIR) : $(CLIENT_DIR) : $(GL_DIR) : $(COMMON_DIR) : $(SERVER_DIR) : $(HTTP_DIR) : $(BASE_DIR)/irc : $(BASE_DIR)/email : $(QUX_DIR) : $(PROGS_DIR) : $(NACL_DIR) : $(SNDCODEC_DIR) : $(D3D_DIR) : $(BOTLIB_DIR) # This is for linking the FTE icon to the MinGW target $(OUT_DIR)/resources.o : winquake.rc diff --git a/engine/client/cl_master.h b/engine/client/cl_master.h index 3a31836b1..dcc9d080c 100644 --- a/engine/client/cl_master.h +++ b/engine/client/cl_master.h @@ -162,7 +162,7 @@ extern player_t *mplayers; void Master_SetupSockets(void); void CL_QueryServers(void); -int NET_CheckPollSockets(void); +int Master_CheckPollSockets(void); void MasterInfo_Request(master_t *mast, qboolean evenifwedonthavethefiles); serverinfo_t *Master_InfoForServer (netadr_t addr); serverinfo_t *Master_InfoForNum (int num); diff --git a/engine/client/cl_ui.c b/engine/client/cl_ui.c index be49307c5..c1c58cbdf 100644 --- a/engine/client/cl_ui.c +++ b/engine/client/cl_ui.c @@ -764,6 +764,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con case UI_CMD_EXECUTETEXT: { char *cmdtext = VM_POINTER(arg[1]); +#ifdef CL_MASTER if (!strncmp(cmdtext, "ping ", 5)) { int i; @@ -785,10 +786,8 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con { MasterInfo_Refresh(); } - /* else if (!strncmp(cmdtext, "r_vidmode", 12)) - { - } - */ else + else +#endif Cbuf_AddText(cmdtext, RESTRICT_SERVER); } break; @@ -987,6 +986,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con #endif break; +#ifdef CL_MASTER case UI_LAN_GETPINGQUEUECOUNT: //these four are master server polling. { int i; @@ -1007,7 +1007,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con if ((int)arg[3] + sizeof(int) >= mask || VM_POINTER(arg[3]) < offset) break; //out of bounds. - NET_CheckPollSockets(); + Master_CheckPollSockets(); if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) <= MAX_PINGREQUESTS) { char *buf = VM_POINTER(arg[1]); @@ -1034,7 +1034,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con if ((int)arg[3] + sizeof(int) >= mask || VM_POINTER(arg[3]) < offset) break; //out of bounds. - NET_CheckPollSockets(); + Master_CheckPollSockets(); if (VM_LONG(arg[0])>= 0 && VM_LONG(arg[0]) <= MAX_PINGREQUESTS) { char *buf = VM_POINTER(arg[1]); @@ -1062,6 +1062,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con strcpy(buf, ""); } break; +#endif case UI_CVAR_REGISTER: if (VM_OOB(arg[0], sizeof(vmcvar_t))) @@ -1100,6 +1101,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con VM_FLOAT(ret) = realtime; break; +#ifdef CL_MASTER case UI_LAN_GETSERVERCOUNT: //LAN Get server count //int (int source) VM_LONG(ret) = Master_TotalCount(); @@ -1136,6 +1138,7 @@ static qintptr_t UI_SystemCalls(void *offset, quintptr_t mask, qintptr_t fn, con case UI_LAN_SERVERISVISIBLE: return 1; break; +#endif case UI_VERIFY_CDKEY: VM_LONG(ret) = true; diff --git a/engine/client/console.c b/engine/client/console.c index c64eca004..3148565c7 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -923,7 +923,7 @@ int Con_DrawInput (int left, int right, int y) /*if its getting completed to something, show some help about the command that is going to be used*/ if (!text[1]) con_commandmatch = 0; - if (con_commandmatch && Cmd_IsCommand(text+(text[1] == '/'?2:1))) + if (con_commandmatch && fname && Cmd_IsCommand(text+(text[1] == '/'?2:1))) { cvar_t *var; char *desc = NULL; diff --git a/engine/client/in_generic.c b/engine/client/in_generic.c new file mode 100644 index 000000000..8c94b776c --- /dev/null +++ b/engine/client/in_generic.c @@ -0,0 +1,365 @@ +#include "quakedef.h" +extern qboolean mouse_active; + +cvar_t m_simtouch = CVARF("m_simtouch", "0", CVAR_ARCHIVE); +cvar_t m_filter = CVARF("m_filter", "0", CVAR_ARCHIVE); +cvar_t m_strafeonright = CVARFD("m_strafeonright", "1", CVAR_ARCHIVE, "If 1, touching the right half of the touchscreen will strafe/move, while the left side will turn."); + +extern cvar_t _windowed_mouse; + +int mousecursor_x, mousecursor_y; /*absolute position*/ +extern int mousemove_x, mousemove_y; +static float mouse_x, mouse_y; +static float mousestrafe_x, mousestrafe_y; +static float old_mouse_x, old_mouse_y; /*for smoothing*/ + + +#define EVENTQUEUELENGTH 128 +struct eventlist_s +{ + enum + { + IEV_KEYDOWN, + IEV_KEYRELEASE, + IEV_MOUSEABS, + IEV_MOUSEDELTA + } type; + int devid; + + union + { + struct + { + float x, y; + } mouse; + struct + { + int scancode, unicode; + } keyboard; + }; +} eventlist[EVENTQUEUELENGTH]; +volatile int events_avail; /*volatile to make sure the cc doesn't try leaving these cached in a register*/ +volatile int events_used; + +static struct eventlist_s *in_newevent(void) +{ + if (events_avail >= events_used + EVENTQUEUELENGTH) + return NULL; + return &eventlist[events_avail & (EVENTQUEUELENGTH-1)]; +} +static struct eventlist_s *in_lastevent(void) +{ + if (events_avail == events_used) + return NULL; + return &eventlist[(events_avail-1) & (EVENTQUEUELENGTH-1)]; +} + +static void in_finishevent(void) +{ + events_avail++; +} + +#define MAXPOINTERS 8 +struct +{ + vec2_t oldpos; + vec2_t downpos; + float movedist; + vec2_t move; + enum + { + MT_UNPRESSED, + MT_PRESSED, + MT_DELTA + } mtype; +} ptr[MAXPOINTERS]; + + + +void IN_Shutdown(void) +{ +} + +void IN_ReInit() +{ + Cvar_Register (&m_simtouch, "input controls"); + Cvar_Register (&m_filter, "input controls"); + Cvar_Register (&m_strafeonright, "input controls"); +} + +void IN_Init(void) +{ + IN_ReInit(); +} + +/*on android, each 'pointer' is a separate touch location*/ +void IN_Commands(void) +{ + struct eventlist_s *ev; + while (events_used != events_avail) + { + ev = &eventlist[events_used & (EVENTQUEUELENGTH-1)]; + switch(ev->type) + { + case IEV_KEYDOWN: + case IEV_KEYRELEASE: + if (ev->keyboard.scancode == K_MOUSE1 && ev->devid < MAXPOINTERS && ptr[ev->devid].mtype != MT_DELTA) + { + if (Key_MouseShouldBeFree()) + ptr[ev->devid].mtype = MT_UNPRESSED; + else + { + if (ev->type == IEV_KEYDOWN) + { + ptr[ev->devid].mtype = MT_PRESSED; + ptr[ev->devid].movedist = 0; + ptr[ev->devid].downpos[0] = ptr[ev->devid].oldpos[0]; + ptr[ev->devid].downpos[1] = ptr[ev->devid].oldpos[1]; + ptr[ev->devid].move[0] = 0; + ptr[ev->devid].move[1] = 0; + } + else + { + if (ptr[ev->devid].mtype == MT_PRESSED) + { + if (ptr[ev->devid].movedist < 5) + { + /*if its on the right, make it a mouse2*/ + int key = (m_strafeonright.ival && ptr[ev->devid].downpos[0] > vid.pixelwidth/2)?K_MOUSE3:K_MOUSE1; + Key_Event(ev->devid, key, 0, true); + Key_Event(ev->devid, key, 0, false); + } + } + ptr[ev->devid].mtype = MT_UNPRESSED; + } + break; + } + } + Key_Event(ev->devid, ev->keyboard.scancode, ev->keyboard.unicode, ev->type == IEV_KEYDOWN); + break; + case IEV_MOUSEABS: + /*mouse cursors only really work with one pointer*/ + if (ev->devid == 0) + { + mousecursor_x = bound(0, ev->mouse.x, vid.width - 1); + mousecursor_y = bound(0, ev->mouse.y, vid.height - 1); + } + + if (ev->devid < MAXPOINTERS) + { + if (ptr[ev->devid%MAXPOINTERS].mtype == MT_DELTA) + ptr[ev->devid%MAXPOINTERS].mtype = MT_UNPRESSED; + ptr[ev->devid].move[0] += ev->mouse.x - ptr[ev->devid].oldpos[0]; + ptr[ev->devid].move[1] += ev->mouse.y - ptr[ev->devid].oldpos[1]; + + ptr[ev->devid].movedist += fabs(ev->mouse.x - ptr[ev->devid].oldpos[0]) + fabs(ev->mouse.y - ptr[ev->devid].oldpos[1]); + + ptr[ev->devid].oldpos[0] = ev->mouse.x; + ptr[ev->devid].oldpos[1] = ev->mouse.y; + } + break; + case IEV_MOUSEDELTA: + /*unlike abs, we can combine the mice properly*/ + mousecursor_x += ev->mouse.x; + mousecursor_y += ev->mouse.y; + mousecursor_x = bound(0, mousecursor_x, vid.width - 1); + mousecursor_y = bound(0, mousecursor_y, vid.height - 1); + + ptr[ev->devid%MAXPOINTERS].move[0] += ev->mouse.x; + ptr[ev->devid%MAXPOINTERS].move[1] += ev->mouse.y; + + ptr[ev->devid%MAXPOINTERS].movedist += fabs(ev->mouse.x) + fabs(ev->mouse.y); + + if (m_simtouch.ival) + { + if (ptr[ev->devid%MAXPOINTERS].mtype == MT_DELTA) + ptr[ev->devid%MAXPOINTERS].mtype = MT_UNPRESSED; + ptr[ev->devid].oldpos[0] = mousecursor_x; + ptr[ev->devid].oldpos[1] = mousecursor_y; + } + else + ptr[ev->devid%MAXPOINTERS].mtype = MT_DELTA; + + break; + } + events_used++; + } +} + + + +static void IN_Update(qboolean ingame) +{ + int i; + //strafing speed is absolute + mousestrafe_x = 0; + mousestrafe_y = 0; + + for (i = 0; i < MAXPOINTERS; i++) + { + /*ignore if no action, to avoid phantom csqc input events*/ + if (ptr[i].mtype == MT_UNPRESSED && !ptr[i].move[0] && !ptr[i].move[1]) + continue; + + if (ptr[i].mtype == MT_DELTA || !CSQC_MousePosition(ptr[i].oldpos[0], ptr[i].oldpos[1], i)) + { + if (!CSQC_MouseMove(ptr[i].move[0], ptr[i].move[1], i)) + { + switch(ptr[i].mtype) + { + case MT_UNPRESSED: + break; + case MT_PRESSED: + if (m_strafeonright.ival && ptr[i].downpos[0] > vid.pixelwidth/2 && ingame) + { + mousestrafe_x += ptr[i].oldpos[0] - ptr[i].downpos[0]; + mousestrafe_y += ptr[i].oldpos[1] - ptr[i].downpos[1]; + } + else + { + mouse_x += ptr[i].move[0]; + mouse_y += ptr[i].move[1]; + } + break; + case MT_DELTA: + mouse_x += ptr[i].move[0]; + mouse_y += ptr[i].move[1]; + break; + } + } + } + ptr[i].move[0] = 0; + ptr[i].move[1] = 0; + } +} + + +void IN_Move (float *movements, int pnum) +{ + qboolean ingame; + extern int mousecursor_x, mousecursor_y; + + if (pnum != 0) + return; //we're lazy today. + + ingame = movements != NULL && (key_dest == key_game); + + IN_Update(ingame); + + if (m_filter.value) + { + mouse_x = (mouse_x + old_mouse_x) * 0.5; + mouse_y = (mouse_y + old_mouse_y) * 0.5; + } + old_mouse_x = mouse_x; + old_mouse_y = mouse_y; + + if(in_xflip.value) mouse_x *= -1; + + mousemove_x += mouse_x; + mousemove_y += mouse_y; + + if (!ingame) + { + mouse_x = mouse_y = 0; +#ifdef VM_UI + UI_MousePosition(mousecursor_x, mousecursor_y); +#endif + } + + /*if the look-mouse is set to always strafe instead...*/ + if ( (in_strafe.state[pnum] & 1) || (lookstrafe.value && (in_mlook.state[pnum] & 1) )) + { + mousestrafe_x += mouse_x; + mouse_x = 0; + } + if ( (in_strafe.state[pnum] & 1) || !(in_mlook.state[pnum] & 1)) + { + mousestrafe_y += mouse_y; + mouse_y = 0; + } + + /*handle strafes*/ + if (movements) + { + float scale; + + scale = m_side.value * sensitivity.value; + movements[1] += mousestrafe_x * scale; + + scale = m_forward.value * sensitivity.value; + if ((in_strafe.state[pnum] & 1) && noclip_anglehack) + movements[2] -= mousestrafe_y * scale; + else + movements[0] -= mousestrafe_y * scale; + } + + if (in_mlook.state[pnum] & 1) + V_StopPitchDrift (pnum); + + /*handle looks*/ + cl.viewanglechange[pnum][YAW] -= m_yaw.value * mouse_x * sensitivity.value; + cl.viewanglechange[pnum][PITCH] += m_pitch.value * mouse_y * sensitivity.value; + + mouse_x = mouse_y = 0.0; +} + + +/*regular key event*/ +void IN_QueueKey(int down, int keycode, int unicode) +{ + struct eventlist_s *ev = in_newevent(); + if (!ev) + return; + ev->type = down?IEV_KEYDOWN:IEV_KEYRELEASE; + ev->devid = 0; + ev->keyboard.scancode = keycode; + ev->keyboard.unicode = unicode; + in_finishevent(); +} +/* +in ppapi, we have 'keycode' and 'char' events completely separately +this doesn't match the rest of the system very well +so we update the previous key event instead, where possible (IME can still trigger multiple chars at a time) + +this is risky and may drop the key in rare situations +*/ +void IN_AmmendUnicode(int unicode) +{ + struct eventlist_s *ev = in_lastevent(); + if (ev && ev->type == IEV_KEYDOWN) + { + if (!ev->keyboard.unicode) + { + ev->keyboard.unicode = unicode; + return; + } + } + /*last command was already used? that makes things painful. maybe noone will notice*/ + IN_QueueKey(true, 0, unicode); + IN_QueueKey(false, 0, unicode); +} + +void IN_QueueMouse(int act, int ptrid, float x, float y, int button) +{ + struct eventlist_s *ev = in_newevent(); + if (!ev) + return; + ev->devid = ptrid; + switch(act) + { + case 0: + case 3: + ev->type = (act==0)?IEV_MOUSEABS:IEV_MOUSEDELTA; + ev->mouse.x = x; + ev->mouse.y = y; + break; + case 1: + case 2: + ev->type = (act==1)?IEV_KEYDOWN:IEV_KEYRELEASE; + ev->keyboard.scancode = K_MOUSE1+button; + ev->keyboard.unicode = 0; + break; + } + in_finishevent(); +} diff --git a/engine/client/keys.c b/engine/client/keys.c index cfb7b3424..5f5bc7f3a 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -674,13 +674,13 @@ void Key_Console (unsigned int unicode, int key) return; } - if (((key=='C' || key=='c') && keydown[K_CTRL]) || (keydown[K_CTRL] && key == K_INS)) + if (((unicode=='C' || unicode=='c') && keydown[K_CTRL]) || (keydown[K_CTRL] && key == K_INS)) { Sys_SaveClipboard(key_lines[edit_line]+1); return; } - if (((key=='V' || key=='v') && keydown[K_CTRL]) || (keydown[K_SHIFT] && key == K_INS)) + if (((unicode=='V' || unicode=='v') && keydown[K_CTRL]) || (keydown[K_SHIFT] && key == K_INS)) { clipText = Sys_GetClipboard(); if (clipText) @@ -1587,9 +1587,9 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) } #endif - if ( (key_dest == key_menu && menubound[key]) + if (key && ((key_dest == key_menu && menubound[key]) || (key_dest == key_console && !consolekeys[key]) - || (key_dest == key_game && ( cls.state == ca_active || !consolekeys[key] ) ) ) + || (key_dest == key_game && ( cls.state == ca_active || !consolekeys[key] ) ) )) { /*don't auto-repeat binds as it breaks too many scripts*/ if (key_repeats[key] > 1) @@ -1649,7 +1649,7 @@ void Key_Event (int devid, int key, unsigned int unicode, qboolean down) #endif case key_game: case key_console: - if ((key >= ' ' && key <= 127) || key == K_ENTER || key == K_TAB) + if ((unicode) || key == K_ENTER || key == K_TAB) key_dest = key_console; Key_Console (unicode, key); break; diff --git a/engine/client/m_master.c b/engine/client/m_master.c index fb188d2ca..25bf0ac43 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -337,7 +337,7 @@ joinserver: static void SL_PreDraw (menu_t *menu) { serverlist_t *info = (serverlist_t*)(menu + 1); - NET_CheckPollSockets(); + Master_CheckPollSockets(); CL_QueryServers(); @@ -692,7 +692,7 @@ static void M_QuickConnect_PreDraw(menu_t *menu) serverinfo_t *s; char adr[MAX_ADR_SIZE]; - NET_CheckPollSockets(); //see if we were told something important. + Master_CheckPollSockets(); //see if we were told something important. CL_QueryServers(); if (Sys_DoubleTime() > quickconnecttimeout) diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index 01b0cb93a..da5fb1701 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -2283,7 +2283,7 @@ static void MSD_SetUnderWater(soundcardinfo_t *sc, qboolean underwater) { } -static void *MSD_Lock (soundcardinfo_t *sc) +static void *MSD_Lock (soundcardinfo_t *sc, unsigned int *sampidx) { return sc->sn.buffer; } diff --git a/engine/client/menu.c b/engine/client/menu.c index 225198346..4a00e702e 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -860,7 +860,9 @@ void M_Init_Internal (void) Cmd_AddRemCommand ("menu_download", Menu_DownloadStuff_f); #endif +#ifdef CL_MASTER Cmd_AddRemCommand ("quickconnect", M_QuickConnect_f); +#endif } void M_DeInit_Internal (void) diff --git a/engine/client/net_master.c b/engine/client/net_master.c index ef5f61de0..3889d51cb 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -862,7 +862,7 @@ void NET_SendPollPacket(int len, void *data, netadr_t to) } } -int NET_CheckPollSockets(void) +int Master_CheckPollSockets(void) { int sock; SOCKET usesocket; diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 3212837a7..818c00264 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -645,6 +645,7 @@ static qboolean CopyCSQCEdictToEntity(csqcedict_t *in, entity_t *out) } else { + out->flags |= RF_FORCECOLOURMOD; out->shaderRGBAf[0] = in->xv->colormod[0]; out->shaderRGBAf[1] = in->xv->colormod[1]; out->shaderRGBAf[2] = in->xv->colormod[2]; @@ -2089,9 +2090,16 @@ static void QCBUILTIN PF_cs_getinputstate (progfuncs_t *prinst, struct globalvar G_FLOAT(OFS_RETURN) = false; return; } + if (cl.paused) + f = cls.netchan.outgoing_sequence; + /*outgoing_sequence says how many packets have actually been sent, but there's an extra pending packet which has not been sent yet - be warned though, its data will change in the coming frames*/ if (f == cls.netchan.outgoing_sequence) + { cmd = &independantphysics[csqc_lplayernum]; + for (f=0 ; f<3 ; f++) + cmd->angles[f] = ((int)(cl.viewangles[csqc_lplayernum][f]*65536.0/360)&65535); + } else cmd = &cl.frames[f&UPDATE_MASK].cmd[csqc_lplayernum]; @@ -4721,7 +4729,17 @@ void CSQC_EntSpawn (struct edict_s *e, int loading) if (!ent->xv) ent->xv = (csqcextentvars_t *)(ent->v+1); #endif + + if (1) + { +// ent->xv->dimension_see = 255; +// ent->xv->dimension_seen = 255; +// ent->xv->dimension_ghost = 0; + ent->xv->dimension_solid = 255; + ent->xv->dimension_hit = 255; + } } + pbool CSQC_EntFree (struct edict_s *e) { struct csqcedict_s *ent = (csqcedict_t*)e; diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 3097ceace..1358b4dff 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -999,12 +999,12 @@ void QCBUILTIN PF_M_gethostcachevalue (progfuncs_t *prinst, struct globalvars_s { case SLIST_HOSTCACHEVIEWCOUNT: CL_QueryServers(); - NET_CheckPollSockets(); + Master_CheckPollSockets(); G_FLOAT(OFS_RETURN) = Master_NumSorted(); return; case SLIST_HOSTCACHETOTALCOUNT: CL_QueryServers(); - NET_CheckPollSockets(); + Master_CheckPollSockets(); G_FLOAT(OFS_RETURN) = Master_TotalCount(); return; @@ -1653,8 +1653,13 @@ builtin_t menu_builtins[] = { PF_cl_getresolution, PF_cl_keynumtostring, PF_cl_findkeysforcommand, +#ifdef CL_MASTER PF_M_gethostcachevalue, PF_M_gethostcachestring, +#else + skip1 + skip1 +#endif PF_parseentitydata, //void parseentitydata(entity ent, string data) = #613; PF_cl_stringtokeynum, @@ -1668,8 +1673,13 @@ builtin_t menu_builtins[] = { PF_M_gethostcachenumber, PF_M_gethostcacheindexforkey, PF_M_addwantedhostcachekey, +#ifdef CL_MASTER PF_M_getextresponse, // #624 PF_netaddress_resolve, +#else + skip1 + skip1 +#endif skip1 /*get gamedir info*/ PF_sprintf, /*sprintf*/ skip1 /*not listed in dp*/ diff --git a/engine/client/r_surf.c b/engine/client/r_surf.c index 1a3f66f68..8acfdfb20 100644 --- a/engine/client/r_surf.c +++ b/engine/client/r_surf.c @@ -1419,7 +1419,7 @@ start: if (node->visframe != r_visframecount) return; - for (c = 0, clipplane = frustum; c < 4; c++, clipplane++) + for (c = 0, clipplane = frustum; c < FRUSTUMPLANES; c++, clipplane++) { if (!(clipflags & (1 << c))) continue; // don't need to clip against it @@ -1640,7 +1640,7 @@ static void Surf_LeafWorldNode (void) // if (!r_nocull->value) { - for (i=0,clipplane=frustum ; i<4 ; i++,clipplane++) + for (i=0,clipplane=frustum ; i<5 ; i++,clipplane++) { clipped = BoxOnPlaneSide (pleaf->minmaxs, pleaf->minmaxs+3, clipplane); if (clipped == 2) @@ -2016,7 +2016,7 @@ void Surf_GenBrushBatches(batch_t **batches, entity_t *ent) b->firstmesh = 0; b->lightmap = s->lightmaptexturenum; b->mesh = NULL; - b->vbo = NULL; + b->vbo = &b->texture->vbo; } b->surf_count++; @@ -2120,7 +2120,7 @@ void Surf_DrawWorld (void) if (!(r_novis.ival & 2)) VectorCopy (r_refdef.vieworg, modelorg); - Surf_RecursiveWorldNode (cl.worldmodel->nodes, 0xf); + Surf_RecursiveWorldNode (cl.worldmodel->nodes, 0x1f); } } @@ -2154,7 +2154,7 @@ void Surf_DrawWorld (void) */ // returns a texture number and the position inside it -static int Surf_LM_AllocBlock (int w, int h, int *x, int *y, shader_t *shader) +int Surf_LM_AllocBlock (int w, int h, int *x, int *y, shader_t *shader) { int i, j; int best, best2; @@ -2711,6 +2711,11 @@ void Surf_BuildLightmaps (void) batch->mesh = BZ_Malloc(sizeof(*batch->mesh)*batch->maxmeshes*2); } BE_GenBrushModelVBO(m); + for (sortid = 0; sortid < SHADER_SORT_COUNT; sortid++) + for (batch = m->batches[sortid]; batch != NULL; batch = batch->next) + { + batch->vbo = &batch->texture->vbo; + } } BE_UploadAllLightmaps(); diff --git a/engine/client/render.h b/engine/client/render.h index 2e688def5..ff9b16cfd 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -38,7 +38,7 @@ struct texture_s; static const texid_t r_nulltex = {{0}}; -#if defined(D3DQUAKE) || defined(ANDROID) +#if 1 || defined(MINIMAL) || defined(D3DQUAKE) || defined(ANDROID) #define sizeof_index_t 2 #endif #if sizeof_index_t == 2 @@ -412,7 +412,7 @@ extern cvar_t r_fullbright; extern cvar_t r_lightmap; extern cvar_t r_glsl_offsetmapping; extern cvar_t r_shadow_realtime_dlight, r_shadow_realtime_dlight_shadows; -extern cvar_t r_shadow_realtime_world,r_shadow_realtime_world_shadows; +extern cvar_t r_shadow_realtime_world, r_shadow_realtime_world_shadows; extern cvar_t r_mirroralpha; extern cvar_t r_wateralpha; extern cvar_t r_dynamic; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 2d5394f45..eb0f9bc8c 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -332,6 +332,8 @@ cvar_t vid_hardwaregamma = SCVARF ("vid_hardwaregamma", "1", cvar_t vid_desktopgamma = SCVARF ("vid_desktopgamma", "0", CVAR_ARCHIVE | CVAR_RENDERERLATCH); +cvar_t r_fog_exp2 = CVARD ("r_fog_exp2", "1", "Expresses how fog fades with distance. 0 (matching DarkPlaces) is typically more realistic, while 1 (matching FitzQuake and others) is more common."); + extern cvar_t gl_dither; cvar_t gl_screenangle = SCVAR("gl_screenangle", "0"); @@ -416,6 +418,7 @@ void GLRenderer_Init(void) Cvar_Register (&gl_overbright, GRAPHICALNICETIES); Cvar_Register (&gl_overbright_all, GRAPHICALNICETIES); Cvar_Register (&gl_dither, GRAPHICALNICETIES); + Cvar_Register (&r_fog_exp2, GLRENDEREROPTIONS); Cvar_Register (&gl_ati_truform, GRAPHICALNICETIES); Cvar_Register (&gl_ati_truform_type, GRAPHICALNICETIES); @@ -1924,7 +1927,7 @@ qbyte *R_MarkLeaves_Q1 (void) } -mplane_t frustum[4]; +mplane_t frustum[FRUSTUMPLANES]; /* @@ -1938,7 +1941,7 @@ qboolean R_CullBox (vec3_t mins, vec3_t maxs) { int i; - for (i=0 ; i<4 ; i++) + for (i=0 ; i 4 + //do far plane + //fog will not logically not actually reach 0, though precision issues will force it. we cut off at an exponant of -500 + if (r_refdef.gfog_rgbd[3]) + { + float culldist; + float fog; + extern cvar_t r_fog_exp2; + + /*Documentation: the GLSL/GL will do this maths: + float dist = 1024; + if (r_fog_exp2.ival) + fog = pow(2, -r_refdef.gfog_rgbd[3] * r_refdef.gfog_rgbd[3] * dist * dist * 1.442695); + else + fog = pow(2, -r_refdef.gfog_rgbd[3] * dist * 1.442695); + */ + + //the fog factor cut-off where its pointless to allow it to get closer to 0 (0 is technically infinite) + fog = 2/255.0f; + + //figure out the eyespace distance required to reach that fog value + culldist = log(fog); + if (r_fog_exp2.ival) + culldist = sqrt(culldist / (-r_refdef.gfog_rgbd[3] * r_refdef.gfog_rgbd[3])); + else + culldist = culldist / (-r_refdef.gfog_rgbd[3]); + //anything drawn beyond this point is fully obscured by fog + + frustum[4].normal[0] = mvp[3] - mvp[2]; + frustum[4].normal[1] = mvp[7] - mvp[6]; + frustum[4].normal[2] = mvp[11] - mvp[10]; + frustum[4].dist = mvp[15] - mvp[14]; + + scale = 1/sqrt(DotProduct(frustum[4].normal, frustum[4].normal)); + frustum[4].normal[0] *= scale; + frustum[4].normal[1] *= scale; + frustum[4].normal[2] *= scale; +// frustum[4].dist *= scale; + frustum[4].dist = DotProduct(r_origin, frustum[4].normal)-culldist; + + frustum[4].type = PLANE_ANYZ; + frustum[4].signbits = SignbitsForPlane (&frustum[4]); + } + else + { + frustum[4].normal[0] = mvp[3] - mvp[2]; + frustum[4].normal[1] = mvp[7] - mvp[6]; + frustum[4].normal[2] = mvp[11] - mvp[10]; + frustum[4].dist = mvp[15] - mvp[14]; + + scale = 1/sqrt(DotProduct(frustum[4].normal, frustum[4].normal)); + frustum[4].normal[0] *= scale; + frustum[4].normal[1] *= scale; + frustum[4].normal[2] *= scale; + frustum[4].dist *= -scale; + + frustum[4].type = PLANE_ANYZ; + frustum[4].signbits = SignbitsForPlane (&frustum[4]); + } +#endif } #else void R_SetFrustum (void) diff --git a/engine/client/snd_al.c b/engine/client/snd_al.c index 4b7dcea1a..41da19a03 100644 --- a/engine/client/snd_al.c +++ b/engine/client/snd_al.c @@ -581,7 +581,7 @@ static void OnChangeALDistanceModel (cvar_t *var, char *value) } /*stub should not be called*/ -static void *OpenAL_LockBuffer (soundcardinfo_t *sc) +static void *OpenAL_LockBuffer (soundcardinfo_t *sc, unsigned int *sampidx) { //Con_Printf("OpenAL: LockBuffer\n"); return NULL; diff --git a/engine/client/snd_alsa.c b/engine/client/snd_alsa.c index 8532df7ad..8cff4e93e 100755 --- a/engine/client/snd_alsa.c +++ b/engine/client/snd_alsa.c @@ -170,7 +170,7 @@ static void ALSA_Shutdown (soundcardinfo_t *sc) free(sc->sn.buffer); } -static void *ALSA_LockBuffer(soundcardinfo_t *sc) +static void *ALSA_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx) { return sc->sn.buffer; } diff --git a/engine/client/snd_directx.c b/engine/client/snd_directx.c index 8e618934f..802953b5b 100644 --- a/engine/client/snd_directx.c +++ b/engine/client/snd_directx.c @@ -87,7 +87,7 @@ static void DSOUND_Restore(soundcardinfo_t *sc) } DWORD dwSize; -static void *DSOUND_Lock(soundcardinfo_t *sc) +static void *DSOUND_Lock(soundcardinfo_t *sc, unsigned int *sampidx) { void *ret; int reps; diff --git a/engine/client/snd_dma.c b/engine/client/snd_dma.c index 132f6938c..7b2a69c10 100644 --- a/engine/client/snd_dma.c +++ b/engine/client/snd_dma.c @@ -642,6 +642,7 @@ sounddriver pSDL_InitCard; sounddriver pWAV_InitCard; sounddriver pDroid_InitCard; sounddriver pAHI_InitCard; +sounddriver pPPAPI_InitCard; typedef struct { char *name; @@ -655,6 +656,7 @@ sdriver_t drivers[] = { {"MacOS", &pMacOS_InitCard}, //prefered on mac {"Droid", &pDroid_InitCard}, //prefered on android (java thread) {"AHI", &pAHI_InitCard}, //prefered on morphos + {"PPAPI", &pPPAPI_InitCard}, //google's native client {"SDL", &pSDL_InitCard}, //prefered on linux {"ALSA", &pALSA_InitCard}, //pure shite @@ -1600,6 +1602,7 @@ static void S_StopAllSounds_f (void) static void S_ClearBuffer (soundcardinfo_t *sc) { void *buffer; + unsigned int dummy; int clear; @@ -1611,7 +1614,8 @@ static void S_ClearBuffer (soundcardinfo_t *sc) else clear = 0; - buffer = sc->Lock(sc); + dummy = 0; + buffer = sc->Lock(sc, &dummy); if (buffer) { Q_memset(buffer, clear, sc->sn.samples * sc->sn.samplebits/8); diff --git a/engine/client/snd_droid.c b/engine/client/snd_droid.c index eb6ff76d6..90cfcd005 100644 --- a/engine/client/snd_droid.c +++ b/engine/client/snd_droid.c @@ -76,10 +76,10 @@ static void Droid_UnlockBuffer(soundcardinfo_t *sc, void *buffer) // pthread_mutex_unlock(&mutex); } -static void *Droid_LockBuffer(soundcardinfo_t *sc) +static void *Droid_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx) { // pthread_mutex_lock(&mutex); - return sc->sn.buffer; + return sc->sn.buffer; } static void Droid_SetUnderWater(soundcardinfo_t *sc, qboolean uw) diff --git a/engine/client/snd_linux.c b/engine/client/snd_linux.c index 055729f20..f23e8636e 100644 --- a/engine/client/snd_linux.c +++ b/engine/client/snd_linux.c @@ -110,7 +110,7 @@ static void OSS_Shutdown(soundcardinfo_t *sc) *sc->name = '\0'; } -static void *OSS_Lock(soundcardinfo_t *sc) +static void *OSS_Lock(soundcardinfo_t *sc, unsigned int *sampidx) { return sc->sn.buffer; } diff --git a/engine/client/snd_macos.c b/engine/client/snd_macos.c index 2cb234824..186d6a308 100644 --- a/engine/client/snd_macos.c +++ b/engine/client/snd_macos.c @@ -101,7 +101,7 @@ static void MacOS_Submit(soundcardinfo_t *sc) { } -static void *MacOS_Lock(soundcardinfo_t *sc) +static void *MacOS_Lock(soundcardinfo_t *sc, unsigned int *sampidx) { return sc->sn.buffer; } diff --git a/engine/client/snd_mix.c b/engine/client/snd_mix.c index 50e857816..10cc15f15 100644 --- a/engine/client/snd_mix.c +++ b/engine/client/snd_mix.c @@ -31,7 +31,7 @@ short *snd_out; void S_TransferPaintBuffer(soundcardinfo_t *sc, int endtime) { - unsigned int startidx, out_idx; + unsigned int out_idx; unsigned int count; unsigned int outlimit; int *p; @@ -43,11 +43,11 @@ void S_TransferPaintBuffer(soundcardinfo_t *sc, int endtime) p = (int *) paintbuffer; count = (endtime - sc->paintedtime) * sc->sn.numchannels; outlimit = sc->sn.samples; - startidx = out_idx = (sc->paintedtime * sc->sn.numchannels) % outlimit; + out_idx = (sc->paintedtime * sc->sn.numchannels) % outlimit; snd_vol = (volume.value*voicevolumemod)*256; numc = sc->sn.numchannels; - pbuf = sc->Lock(sc); + pbuf = sc->Lock(sc, &out_idx); if (!pbuf) return; diff --git a/engine/client/snd_morphos.c b/engine/client/snd_morphos.c index c19d3aa0b..1cd3f730e 100644 --- a/engine/client/snd_morphos.c +++ b/engine/client/snd_morphos.c @@ -106,7 +106,7 @@ static void AHI_UnlockBuffer(soundcardinfo_t *sc, void *buffer) { } -static void *AHI_LockBuffer(soundcardinfo_t *sc) +static void *AHI_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx) { return sc->sn.buffer; } diff --git a/engine/client/snd_sdl.c b/engine/client/snd_sdl.c index 1c5edb121..4be539abb 100644 --- a/engine/client/snd_sdl.c +++ b/engine/client/snd_sdl.c @@ -53,7 +53,7 @@ static void VARGS SSDL_Paint(void *userdata, qbyte *stream, int len) sc->snd_sent += len; } -static void *SSDL_LockBuffer(soundcardinfo_t *sc) +static void *SSDL_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx) { SDL_LockAudio(); diff --git a/engine/client/snd_win.c b/engine/client/snd_win.c index 6aa569dac..db37005ec 100644 --- a/engine/client/snd_win.c +++ b/engine/client/snd_win.c @@ -75,7 +75,7 @@ void S_UnblockSound (void) } -static void *WAV_Lock (soundcardinfo_t *sc) +static void *WAV_Lock (soundcardinfo_t *sc, unsigned int *sampidx) { return sc->sn.buffer; } diff --git a/engine/client/sound.h b/engine/client/sound.h index 5f95ac738..2f55eacf5 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -285,7 +285,7 @@ struct soundcardinfo_s { //windows has one defined AFTER directsound int samplequeue; //this is the number of samples the device can enqueue. if set, DMAPos returns the write point (rather than hardware read point) (in samplepairs). //callbacks - void *(*Lock) (soundcardinfo_t *sc); + void *(*Lock) (soundcardinfo_t *sc, unsigned int *startoffset); void (*Unlock) (soundcardinfo_t *sc, void *buffer); void (*Submit) (soundcardinfo_t *sc, int start, int end); void (*Shutdown) (soundcardinfo_t *sc); diff --git a/engine/client/sys_droid.c b/engine/client/sys_droid.c index 580e14cbc..c3d5f870a 100644 --- a/engine/client/sys_droid.c +++ b/engine/client/sys_droid.c @@ -48,8 +48,9 @@ JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_frame(JNIEnv *env, jobject } JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_init(JNIEnv *env, jobject obj, - jint width, jint height, jstring path) + jint width, jint height, jstring japkpath, jstring jusrpath) { + char *tmp; vid.pixelwidth = width; vid.pixelheight = height; if (sys_running) @@ -60,13 +61,14 @@ JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_init(JNIEnv *env, jobject o { "ftedroid", "-basepack", - (*env)->GetStringUTFChars(env, path, NULL), + NULL, /*filled in later*/ "", "" - //we should do this somewhere... (*env)->ReleaseStringUTFChars(env, path, parms.basedir); }; quakeparms_t parms; - parms.basedir = "/sdcard/fte"; + if (sys_memheap) + free(sys_memheap); + parms.basedir = NULL; /*filled in later*/ parms.argc = 3; parms.argv = args; parms.memsize = 16*1024*1024; @@ -77,7 +79,23 @@ JNIEXPORT void JNICALL Java_com_fteqw_FTEDroidEngine_init(JNIEnv *env, jobject o return; } - Sys_Printf("Starting up (%s)\n", args[2]); + + args[2] = parms.membase; + tmp = (*env)->GetStringUTFChars(env, japkpath, NULL); + strcpy(args[2], tmp); + (*env)->ReleaseStringUTFChars(env, japkpath, tmp); + parms.membase += strlen(args[2])+1; + parms.memsize -= strlen(args[2])+1; + + parms.basedir = parms.membase; + tmp = (*env)->GetStringUTFChars(env, jusrpath, NULL); + strcpy(parms.basedir, tmp); + (*env)->ReleaseStringUTFChars(env, jusrpath, tmp); + parms.membase += strlen(parms.basedir)+1; + parms.memsize -= strlen(parms.basedir)+1; + + + Sys_Printf("Starting up (apk=%s, usr=%s)\n", args[2], parms.basedir); COM_InitArgv(parms.argc, parms.argv); TL_InitLanguages(); diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index fee2eafa9..19373f574 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -51,6 +51,13 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define NO_OGG #endif +#ifdef NACL + #define NO_PNG + #define NO_JPEG + #define NO_OGG + #define NO_ZLIB +#endif + #ifdef HAVE_CONFIG_H //if it was configured properly, then we have a more correct list of features we want to use. #include "config.h" #else @@ -229,6 +236,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef VOICECHAT #undef TEXTEDITOR #endif +#ifdef NACL +#undef CL_MASTER //no sockets support +#undef SV_MASTER //noone uses this anyway +#undef VOICECHAT //not going to compile a speex library - I'm too lazy, but it can be done. +#undef WEBSERVER //no sockets support (certainly no servers) +#undef WEBCLIENT //no sockets support (could use a different method, but that is non-trivial) +#undef TCPCONNECT +#undef IRCCONNECT +#endif //fix things a little... @@ -272,7 +288,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef WEBCLIENT #undef TEXTEDITOR #undef RUNTIMELIGHTING - #undef TERRAIN //not supported #undef PSET_SCRIPT #undef PSET_CLASSIC @@ -298,10 +313,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #undef Q3CLIENT #endif - #if !defined(GLQUAKE) - #undef TERRAIN - #endif - // undefine things not supported yet for D3D #if defined(D3DQUAKE) && !defined(GLQUAKE) #undef DDS // this is dumb diff --git a/engine/common/common.c b/engine/common/common.c index cca81b5b4..95af1310b 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -3352,7 +3352,6 @@ void COM_Init (void) Cmd_AddCommand ("version", COM_Version_f); //prints the pak or whatever where this file can be found. Cmd_AddCommand ("crashme", (void*)1); //debugging feature, makes it jump to an invalid address - COM_InitFilesystem (); COM_CheckRegistered (); diff --git a/engine/common/common.h b/engine/common/common.h index 7918d38d0..8c8d216fd 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -364,6 +364,7 @@ enum fs_relative{ FS_GAME, //standard search (not generally valid for save/rename/delete/etc) FS_ROOT, //./ FS_GAMEONLY, //$gamedir/ + FS_GAMEDOWNLOADCACHE, //typically the same as FS_GAMEONLY FS_CONFIGONLY, //fte/ (should still be part of the game path) FS_SKINS //qw/skins/ }; diff --git a/engine/common/fs.c b/engine/common/fs.c index 6b5c57d97..648426a03 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -2513,7 +2513,7 @@ COM_InitFilesystem */ void COM_InitFilesystem (void) { - FILE *f; + vfsfile_t *f; int i, j; char *ev; @@ -2562,10 +2562,10 @@ void COM_InitFilesystem (void) { if (!gamemode_info[i].auniquefile[j]) continue; //no more - f = fopen(va("%s%s", com_quakedir, gamemode_info[i].auniquefile[j]), "rb"); + f = VFSOS_Open(va("%s%s", com_quakedir, gamemode_info[i].auniquefile[j]), "rb"); if (f) { - fclose(f); + VFS_CLOSE(f); gamenum = i; break; } @@ -2589,11 +2589,11 @@ void COM_InitFilesystem (void) { if (gamemode_info[gamenum].auniquefile[j]) { - f = fopen(va("%s%s", com_quakedir, gamemode_info[i].auniquefile[j]), "rb"); + f = VFSOS_Open(va("%s%s", com_quakedir, gamemode_info[i].auniquefile[j]), "rb"); if (f) { //we found it, its all okay - fclose(f); + VFS_CLOSE(f); break; } if (autobasedir) diff --git a/engine/common/fs_stdio.c b/engine/common/fs_stdio.c index 141baeecb..0aa832eb3 100644 --- a/engine/common/fs_stdio.c +++ b/engine/common/fs_stdio.c @@ -2,6 +2,8 @@ #include "fs.h" #include "errno.h" +#ifndef NACL + #ifdef WEBSVONLY #define Z_Free free #define Z_Malloc malloc @@ -294,3 +296,4 @@ searchpathfuncs_t stdiofilefuncs = { FSSTDIO_OpenVFS }; #endif +#endif \ No newline at end of file diff --git a/engine/common/net.h b/engine/common/net.h index fd09fffc3..7843ec169 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -21,7 +21,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define PORT_ANY -1 -typedef enum {NA_INVALID, NA_LOOPBACK, NA_IP, NA_IPV6, NA_IPX, NA_BROADCAST_IP, NA_BROADCAST_IP6, NA_BROADCAST_IPX, NA_TCP, NA_TCPV6, NA_IRC} netadrtype_t; +#ifdef NACL +#define HAVE_WEBSOCKCL +#endif + +typedef enum {NA_INVALID, NA_LOOPBACK, NA_IP, NA_IPV6, NA_IPX, NA_BROADCAST_IP, NA_BROADCAST_IP6, NA_BROADCAST_IPX, NA_TCP, NA_TCPV6, NA_IRC, NA_WEBSOCKET} netadrtype_t; typedef enum {NS_CLIENT, NS_SERVER} netsrc_t; @@ -40,6 +44,9 @@ typedef struct char user[32]; char channel[12]; } irc; +#endif +#ifdef HAVE_WEBSOCKCL + char websocketurl[64]; #endif } address; diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 68b703a2a..c56de639b 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -23,6 +23,8 @@ struct sockaddr; #include "quakedef.h" #include "netinc.h" + + netadr_t net_local_cl_ipadr; //still used to match local ui requests (quake/gamespy), and to generate ip reports for q3 servers (which is probably pointless). netadr_t net_from; @@ -61,7 +63,9 @@ void (*pfreeaddrinfo) (struct addrinfo*); void NET_GetLocalAddress (int socket, netadr_t *out); int TCP_OpenListenSocket (int port); +#ifdef HAVE_IPV4 extern cvar_t sv_port_ipv4; +#endif #ifdef IPPROTO_IPV6 int UDP6_OpenSocket (int port, qboolean bcast); extern cvar_t sv_port_ipv6; @@ -101,6 +105,14 @@ int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s) { switch(a->type) { +#ifdef HAVE_WEBSOCKCL + case NA_WEBSOCKET: + memset (s, 0, sizeof(struct sockaddr_websocket)); + ((struct sockaddr_websocket*)s)->sws_family = AF_WEBSOCK; + memcpy(((struct sockaddr_websocket*)s)->url, a->address.websocketurl, sizeof(((struct sockaddr_websocket*)s)->url)); + return sizeof(struct sockaddr_websocket); +#endif +#ifdef HAVE_IPV4 case NA_BROADCAST_IP: memset (s, 0, sizeof(struct sockaddr_in)); ((struct sockaddr_in*)s)->sin_family = AF_INET; @@ -117,6 +129,7 @@ int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s) *(int *)&((struct sockaddr_in*)s)->sin_addr = *(int *)&a->address.ip; ((struct sockaddr_in*)s)->sin_port = a->port; return sizeof(struct sockaddr_in); +#endif #ifdef IPPROTO_IPV6 case NA_BROADCAST_IP6: memset (s, 0, sizeof(struct sockaddr_in)); @@ -165,11 +178,20 @@ void SockadrToNetadr (struct sockaddr_qstorage *s, netadr_t *a) switch (((struct sockaddr*)s)->sa_family) { +#ifdef HAVE_WEBSOCKCL + case AF_WEBSOCK: + a->type = NA_WEBSOCKET; + memcpy(a->address.websocketurl, ((struct sockaddr_websocket*)s)->url, sizeof(a->address.websocketurl)); + a->port = 0; + break; +#endif +#ifdef HAVE_IPV4 case AF_INET: a->type = NA_IP; *(int *)&a->address.ip = ((struct sockaddr_in *)s)->sin_addr.s_addr; a->port = ((struct sockaddr_in *)s)->sin_port; break; +#endif #ifdef IPPROTO_IPV6 case AF_INET6: a->type = NA_IPV6; @@ -203,12 +225,23 @@ qboolean NET_CompareAdr (netadr_t a, netadr_t b) if (a.type == NA_LOOPBACK) return true; +#ifdef HAVE_WEBSOCKCL + if (a.type == NA_WEBSOCKET) + { + if (!strcmp(a.address.websocketurl, a.address.websocketurl) && a.port == b.port) + return true; + return false; + } +#endif + +#ifdef HAVE_IPV4 if (a.type == NA_IP || a.type == NA_BROADCAST_IP || a.type == NA_TCP) { if ((memcmp(a.address.ip, b.address.ip, sizeof(a.address.ip)) == 0) && a.port == b.port) return true; return false; } +#endif #ifdef IPPROTO_IPV6 if (a.type == NA_IPV6 || a.type == NA_BROADCAST_IP6 || a.type == NA_TCPV6) @@ -256,12 +289,14 @@ qboolean NET_CompareBaseAdr (netadr_t a, netadr_t b) if (a.type == NA_LOOPBACK) return true; +#ifdef HAVE_IPV4 if (a.type == NA_IP || a.type == NA_TCP) { if ((memcmp(a.address.ip, b.address.ip, sizeof(a.address.ip)) == 0)) return true; return false; } +#endif #ifdef IPPROTO_IPV6 if (a.type == NA_IPV6 || a.type == NA_BROADCAST_IP6) { @@ -300,6 +335,7 @@ qboolean NET_AddressSmellsFunny(netadr_t a) //rejects certain blacklisted addresses switch(a.type) { +#ifdef HAVE_IPV4 case NA_BROADCAST_IP: case NA_IP: //reject localhost @@ -313,6 +349,7 @@ qboolean NET_AddressSmellsFunny(netadr_t a) return true; //not much else I can reject return false; +#endif #ifdef IPPROTO_IPV6 case NA_BROADCAST_IP6: @@ -352,6 +389,11 @@ char *NET_AdrToString (char *s, int len, netadr_t a) switch(a.type) { +#ifdef HAVE_WEBSOCKCL + case NA_WEBSOCKET: + Q_strncpyz(s, a.address.websocketurl, len); + break; +#endif #ifdef TCPCONNECT case NA_TCP: if (len < 7) @@ -361,6 +403,7 @@ char *NET_AdrToString (char *s, int len, netadr_t a) len -= 6; //fallthrough #endif +#ifdef HAVE_IPV4 case NA_BROADCAST_IP: case NA_IP: if (a.port) @@ -381,6 +424,7 @@ char *NET_AdrToString (char *s, int len, netadr_t a) a.address.ip[3]); } break; +#endif #ifdef TCPCONNECT case NA_TCPV6: if (len < 7) @@ -717,11 +761,16 @@ qboolean NET_StringToSockaddr (const char *s, struct sockaddr_qstorage *sadr) if (((struct sockaddr_in *)sadr)->sin_family == AF_INET6) break; //first one should be best... //fallthrough +#ifdef HAVE_IPV4 case AF_INET: memcpy(sadr, pos->ai_addr, pos->ai_addrlen); if (pos->ai_family == AF_INET) goto dblbreak; //don't try finding any more, this is quake, they probably prefer ip4... break; +#else + memcpy(sadr, pos->ai_addr, pos->ai_addrlen); + goto dblbreak; +#endif } } dblbreak: @@ -732,6 +781,7 @@ dblbreak: else #endif { +#ifdef HAVE_IPV4 ((struct sockaddr_in *)sadr)->sin_family = AF_INET; ((struct sockaddr_in *)sadr)->sin_port = 0; @@ -760,6 +810,9 @@ dblbreak: return false; *(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0]; } +#else + return false; +#endif } return true; @@ -777,6 +830,37 @@ qboolean NET_StringToAdr (const char *s, netadr_t *a) Con_DPrintf("Resolving address: %s\n", s); + if (!strcmp (s, "internalserver")) + { + memset (a, 0, sizeof(*a)); + a->type = NA_LOOPBACK; + return true; + } + +#ifdef HAVE_WEBSOCKCL + if (!strncmp (s, "ws://", 5) || !strncmp (s, "wss://", 6)) + { + memset (a, 0, sizeof(*a)); + a->type = NA_WEBSOCKET; + Q_strncpyz(a->address.websocketurl, s, sizeof(a->address.websocketurl)); + return true; + } + else + { + /*code for convienience - no other protocols work anyway*/ + static qboolean warned; + if (!warned) + { + Con_Printf("Note: Native client builds can only connect to websocket servers.\n"); + warned = true; + } + memset (a, 0, sizeof(*a)); + a->type = NA_WEBSOCKET; + memcpy(a->address.websocketurl, "ws://", 5); + Q_strncpyz(a->address.websocketurl+5, s, sizeof(a->address.websocketurl)-5); + return true; + } +#endif #ifdef TCPCONNECT if (!strncmp (s, "tcp://", 6)) { @@ -828,13 +912,6 @@ qboolean NET_StringToAdr (const char *s, netadr_t *a) } #endif - if (!strcmp (s, "internalserver")) - { - memset (a, 0, sizeof(*a)); - a->type = NA_LOOPBACK; - return true; - } - if (!NET_StringToSockaddr (s, &sadr)) { a->type = NA_INVALID; @@ -1371,6 +1448,9 @@ typedef struct ftenet_generic_connection_s { qboolean (*GetPacket)(struct ftenet_generic_connection_s *con); qboolean (*SendPacket)(struct ftenet_generic_connection_s *con, int length, void *data, netadr_t to); void (*Close)(struct ftenet_generic_connection_s *con); +#ifdef HAVE_PACKET + int (*SetReceiveFDSet) (struct ftenet_generic_connection_s *con, fd_set *fdset); /*set for connections which have multiple sockets (ie: listening tcp connections)*/ +#endif netadrtype_t addrtype[FTENET_ADDRTYPES]; qboolean islisten; @@ -1457,8 +1537,10 @@ void FTENET_CloseCollection(ftenet_connections_t *col) void FTENET_Generic_Close(ftenet_generic_connection_t *con) { +#ifdef HAVE_PACKET if (con->thesocket != INVALID_SOCKET) closesocket(con->thesocket); +#endif Z_Free(con); } @@ -1514,6 +1596,9 @@ ftenet_generic_connection_t *FTENET_Loop_EstablishConnection(qboolean isserver, int FTENET_Generic_GetLocalAddress(ftenet_generic_connection_t *con, netadr_t *out, int count) { +#ifndef HAVE_PACKET + return 0; +#else struct sockaddr_qstorage from; int fromsize = sizeof(from); netadr_t adr; @@ -1608,6 +1693,7 @@ int FTENET_Generic_GetLocalAddress(ftenet_generic_connection_t *con, netadr_t *o { h = gethostbyname(adrs); b = 0; +#ifdef HAVE_IPV4 if(h && h->h_addrtype == AF_INET) { for (b = 0; h->h_addr_list[b]; b++) @@ -1619,8 +1705,9 @@ int FTENET_Generic_GetLocalAddress(ftenet_generic_connection_t *con, netadr_t *o *out = adr; } } - #ifdef IPPROTO_IPV6 - else if(h && h->h_addrtype == AF_INET6) +#endif +#ifdef IPPROTO_IPV6 + if(h && h->h_addrtype == AF_INET6) { for (b = 0; h->h_addr_list[b]; b++) { @@ -1631,7 +1718,7 @@ int FTENET_Generic_GetLocalAddress(ftenet_generic_connection_t *con, netadr_t *o *out = adr; } } - #endif +#endif if (b == 0) { @@ -1648,10 +1735,14 @@ int FTENET_Generic_GetLocalAddress(ftenet_generic_connection_t *con, netadr_t *o } return idx; +#endif } qboolean FTENET_Generic_GetPacket(ftenet_generic_connection_t *con) { +#ifndef HAVE_PACKET + return false; +#else struct sockaddr_qstorage from; int fromlen; int ret; @@ -1709,10 +1800,14 @@ qboolean FTENET_Generic_GetPacket(ftenet_generic_connection_t *con) } return true; +#endif } qboolean FTENET_Generic_SendPacket(ftenet_generic_connection_t *con, int length, void *data, netadr_t to) { +#ifndef HAVE_PACKET + return false; +#else struct sockaddr_qstorage addr; int size; int ret; @@ -1782,6 +1877,7 @@ qboolean FTENET_Generic_SendPacket(ftenet_generic_connection_t *con, int length, Con_TPrintf (TL_NETSENDERROR, ecode); } return true; +#endif } qboolean NET_PortToAdr (int adrfamily, const char *s, netadr_t *a) @@ -1795,22 +1891,28 @@ qboolean NET_PortToAdr (int adrfamily, const char *s, netadr_t *a) { memset(a, 0, sizeof(*a)); a->port = htons((unsigned short)port); - if (adrfamily == AF_INET) + switch(adrfamily) + { +#ifdef HAVE_IPV4 + case AF_INET: a->type = NA_IP; + return true; +#endif #ifdef IPPROTO_IPV6 - else if (adrfamily == AF_INET6) + case AF_INET6: a->type = NA_IPV6; + return true; #endif #ifdef USEIPX - else if (adrfamily == AF_IPX) + case AF_IPX: a->type = NA_IPX; + return true; #endif - else - { + default: a->type = NA_INVALID; return false; } - return true; + return false; } a->type = NA_INVALID; return false; @@ -1818,6 +1920,9 @@ qboolean NET_PortToAdr (int adrfamily, const char *s, netadr_t *a) ftenet_generic_connection_t *FTENET_Generic_EstablishConnection(int adrfamily, int protocol, qboolean isserver, const char *address) { +#ifndef HAVE_PACKET + return NULL; +#else //this is written to support either ipv4 or ipv6, depending on the remote addr. ftenet_generic_connection_t *newcon; @@ -1954,6 +2059,7 @@ ftenet_generic_connection_t *FTENET_Generic_EstablishConnection(int adrfamily, i closesocket(newsocket); return NULL; } +#endif } #ifdef IPPROTO_IPV6 @@ -1962,10 +2068,12 @@ ftenet_generic_connection_t *FTENET_UDP6_EstablishConnection(qboolean isserver, return FTENET_Generic_EstablishConnection(AF_INET6, IPPROTO_UDP, isserver, address); } #endif +#ifdef HAVE_IPV4 ftenet_generic_connection_t *FTENET_UDP4_EstablishConnection(qboolean isserver, const char *address) { return FTENET_Generic_EstablishConnection(AF_INET, IPPROTO_UDP, isserver, address); } +#endif #ifdef USEIPX ftenet_generic_connection_t *FTENET_IPX_EstablishConnection(qboolean isserver, const char *address) { @@ -1977,8 +2085,16 @@ ftenet_generic_connection_t *FTENET_IPX_EstablishConnection(qboolean isserver, c typedef struct ftenet_tcpconnect_stream_s { int socketnum; int inlen; - qboolean waitingforprotocolconfirmation; - char inbuffer[1500]; + int outlen; + + enum + { + TCPC_UNKNOWN, + TCPC_QIZMO, + TCPC_WEBSOCKET + } clienttype; + char inbuffer[3000]; + char outbuffer[3000]; float timeouttime; netadr_t remoteaddr; struct ftenet_tcpconnect_stream_s *next; @@ -1991,6 +2107,42 @@ typedef struct { ftenet_tcpconnect_stream_t *tcpstreams; } ftenet_tcpconnect_connection_t; +void tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen) +{ + static tab[64] = + { + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' + }; + unsigned int usedbits = 0; + unsigned int val = 0; + outlen--; + while(inlen) + { + while(usedbits < 24 && inlen) + { + val <<= 8; + val |= (*in++); + inlen--; + usedbits += 8; + } + if (outlen < 4) + return; + val <<= 24 - usedbits; + + *out++ = (usedbits > 0)?tab[(val>>18)&0x3f]:'='; + *out++ = (usedbits > 6)?tab[(val>>12)&0x3f]:'='; + *out++ = (usedbits > 12)?tab[(val>>6)&0x3f]:'='; + *out++ = (usedbits > 18)?tab[(val>>0)&0x3f]:'='; + val=0; + usedbits = 0; + } + *out = 0; +} + +int SHA1(char *digest, int maxdigestsize, char *string); qboolean FTENET_TCPConnect_GetPacket(ftenet_generic_connection_t *gcon) { ftenet_tcpconnect_connection_t *con = (ftenet_tcpconnect_connection_t*)gcon; @@ -2055,45 +2207,394 @@ closesvstream: } st->inlen += ret; - if (st->waitingforprotocolconfirmation) + switch(st->clienttype) { + case TCPC_UNKNOWN: if (st->inlen < 6) continue; - if (strncmp(st->inbuffer, "qizmo\n", 6)) + if (!strncmp(st->inbuffer, "qizmo\n", 6)) { - Con_Printf ("Unknown TCP client\n"); + memmove(st->inbuffer, st->inbuffer+6, st->inlen - (6)); + st->inlen -= 6; + st->clienttype = TCPC_QIZMO; + if (con->generic.islisten) + { + //send the qizmo handshake response. + send(st->socketnum, "qizmo\n", 6, 0); + } + } + else if (con->generic.islisten && !strncmp(st->inbuffer, "GET ", 4)) + { + int i, j; + int attr = 0; + int alen = 0; + qboolean headerscomplete = false; + enum + { + WCATTR_METHOD, + WCATTR_URL, + WCATTR_HTTP, + WCATTR_HOST, + WCATTR_UPGRADE, + WCATTR_CONNECTION, + WCATTR_WSKEY, + WCATTR_WSVER, + //WCATTR_ORIGIN, + WCATTR_WSPROTO, + //WCATTR_WSEXT, + WCATTR_COUNT + }; + char arg[WCATTR_COUNT][64]; + for (i = 0; i < WCATTR_COUNT; i++) + arg[i][0] = 0; + for (i = 0; i < st->inlen; i++) + { + if (alen == 63) + goto handshakeerror; + if (st->inbuffer[i] == ' ' || st->inbuffer[i] == '\t') + { + arg[attr][alen++] = 0; + alen=0; + if (attr++ == WCATTR_HTTP) + break; + + for (; i < st->inlen && (st->inbuffer[i] == ' ' || st->inbuffer[i] == '\t'); i++) + ; + if (i == st->inlen) + break; + } + arg[attr][alen++] = st->inbuffer[i]; + if (st->inbuffer[i] == '\n') + { + arg[attr][alen++] = 0; + alen=0; + break; + } + } + i++; + attr = 0; + j = i; + for (; i < st->inlen; i++) + { + if ((i+1 < st->inlen && st->inbuffer[i] == '\r' && st->inbuffer[i+1] == '\n') || + (i < st->inlen && st->inbuffer[i] == '\n')) + { + i+=2; + headerscomplete = true; + break; + } + + for (; i < st->inlen && (st->inbuffer[i] == ' ' || st->inbuffer[i] == '\t'); i++) + ; + if (i == st->inlen) + break; + + for (j = i; j < st->inlen; j++) + { + if (st->inbuffer[j] == ':' || st->inbuffer[j] == '\n') + { + /*set j to the end of the word, going back past whitespace*/ + while (j > i && (st->inbuffer[j-1] == ' ' || st->inbuffer[i-1] == '\t')) + j--; + break; + } + } + if (!strnicmp(&st->inbuffer[i], "Host", j-i)) + attr = WCATTR_HOST; + else if (!strnicmp(&st->inbuffer[i], "Upgrade", j-i)) + attr = WCATTR_UPGRADE; + else if (!strnicmp(&st->inbuffer[i], "Connection", j-i)) + attr = WCATTR_CONNECTION; + else if (!strnicmp(&st->inbuffer[i], "Sec-WebSocket-Key", j-i)) + attr = WCATTR_WSKEY; + else if (!strnicmp(&st->inbuffer[i], "Sec-WebSocket-Version", j-i)) + attr = WCATTR_WSVER; +// else if (!strnicmp(&st->inbuffer[i], "Origin", j-i)) +// attr = WCATTR_ORIGIN; + else if (!strnicmp(&st->inbuffer[i], "Sec-WebSocket-Protocol", j-i)) + attr = WCATTR_WSPROTO; +// else if (!strnicmp(&st->inbuffer[i], "Sec-WebSocket-Extensions", j-i)) +// attr = WCATTR_WSEXT; + else + attr = 0; + + i = j; + /*skip over the whitespace at the end*/ + for (; i < st->inlen && (st->inbuffer[i] == ' ' || st->inbuffer[i] == '\t'); i++) + ; + if (i < st->inlen && st->inbuffer[i] == ':') + { + i++; + for (; i < st->inlen && (st->inbuffer[i] == ' ' || st->inbuffer[i] == '\t'); i++) + ; + j = i; + + for (; i < st->inlen && st->inbuffer[i] != '\n'; i++) + ; + if (i > j && st->inbuffer[i-1] == '\r') + i--; + if (attr) + Q_strncpyz(arg[attr], &st->inbuffer[j], (i-j > 63)?64:(i - j + 1)); + if (i < st->inlen && st->inbuffer[i] == '\r') + i++; + } + else + { + /*just a word on the line on its own*/ + goto handshakeerror; + } + } + + if (headerscomplete) + { + char *resp; + //must be a Host, Upgrade=websocket, Connection=Upgrade, Sec-WebSocket-Key=base64(randbytes(16)), Sec-WebSocket-Version=13 + //optionally will be Origin=url, Sec-WebSocket-Protocol=FTEWebSocket, Sec-WebSocket-Extensions + //other fields will be ignored. + + //FIXME: reply with 426 Upgrade Required if wsversion is not supported + + if (!stricmp(arg[WCATTR_UPGRADE], "websocket") && !stricmp(arg[WCATTR_CONNECTION], "Upgrade")) + { + if (atoi(arg[WCATTR_WSVER]) != 13) + { + memmove(st->inbuffer, st->inbuffer+i, st->inlen - (i)); + st->inlen -= i; + resp = va( "HTTP/1.1 426 Upgrade Required\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + //send the websocket handshake rejection. + send(st->socketnum, resp, strlen(resp), 0); + + goto closesvstream; + } + else + { + char acceptkey[20*2]; + unsigned char sha1digest[20]; + memmove(st->inbuffer, st->inbuffer+i, st->inlen - (i)); + st->inlen -= i; + + tobase64(acceptkey, sizeof(acceptkey), sha1digest, SHA1(sha1digest, sizeof(sha1digest), va("%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", arg[WCATTR_WSKEY]))); + + resp = va( "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" +// "Sec-WebSocket-Protocol: FTEWebSocket\r\n" + "\r\n", acceptkey); + //send the websocket handshake response. + send(st->socketnum, resp, strlen(resp), 0); + + //and the connection is okay + st->clienttype = TCPC_WEBSOCKET; + } + } + else + { + memmove(st->inbuffer, st->inbuffer+i, st->inlen - (i)); + st->inlen -= i; + resp = va( "HTTP/1.1 426 Upgrade Required\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"); + //send the websocket handshake rejection. + send(st->socketnum, resp, strlen(resp), 0); + + goto closesvstream; + } + } + } + else + { +handshakeerror: + Con_Printf ("Unknown TCP handshake from %s\n", NET_AdrToString (adr, sizeof(adr), net_from)); goto closesvstream; } - memmove(st->inbuffer, st->inbuffer+6, st->inlen - (6)); - st->inlen -= 6; - st->waitingforprotocolconfirmation = false; + break; + case TCPC_QIZMO: + if (st->inlen < 2) + continue; + + net_message.cursize = BigShort(*(short*)st->inbuffer); + if (net_message.cursize >= sizeof(net_message_buffer) ) + { + Con_TPrintf (TL_OVERSIZEPACKETFROM, NET_AdrToString (adr, sizeof(adr), net_from)); + goto closesvstream; + } + if (net_message.cursize+2 > st->inlen) + { //not enough buffered to read a packet out of it. + continue; + } + + memcpy(net_message_buffer, st->inbuffer+2, net_message.cursize); + memmove(st->inbuffer, st->inbuffer+net_message.cursize+2, st->inlen - (net_message.cursize+2)); + st->inlen -= net_message.cursize+2; + + net_message.packing = SZ_RAWBYTES; + net_message.currentbit = 0; + net_from = st->remoteaddr; + + return true; + case TCPC_WEBSOCKET: + while (st->inlen >= 2) + { + unsigned short ctrl = ((unsigned char*)st->inbuffer)[0]<<8 | ((unsigned char*)st->inbuffer)[1]; + unsigned long paylen; + unsigned int payoffs = 2; + unsigned int mask = 0; + st->inbuffer[st->inlen]=0; + if ((ctrl & 0x7f) == 127) + { + //as a payload is not allowed to be encoded as too large a type, and quakeworld never used packets larger than 1450 bytes anyway, this code isn't needed (65k is the max even without this) +// if (sizeof(paylen) < 8) + { + Con_Printf ("%s: payload frame too large\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); + goto closesvstream; + } +/* else + { + if (payoffs + 8 > st->inlen) + break; + paylen = + ((unsigned char*)st->inbuffer)[payoffs+0]<<56 | + ((unsigned char*)st->inbuffer)[payoffs+1]<<48 | + ((unsigned char*)st->inbuffer)[payoffs+2]<<40 | + ((unsigned char*)st->inbuffer)[payoffs+3]<<32 | + ((unsigned char*)st->inbuffer)[payoffs+4]<<24 | + ((unsigned char*)st->inbuffer)[payoffs+5]<<16 | + ((unsigned char*)st->inbuffer)[payoffs+6]<<8 | + ((unsigned char*)st->inbuffer)[payoffs+7]<<0; + if (paylen < 0x10000) + { + Con_Printf ("%s: payload size encoded badly\n", NET_AdrToString (st->remoteaddr, sizeof(st->remoteaddr), net_from)); + goto closesvstream; + } + payoffs += 8; + } +*/ } + else if ((ctrl & 0x7f) == 126) + { + if (payoffs + 2 > st->inlen) + break; + paylen = + ((unsigned char*)st->inbuffer)[payoffs+0]<<8 | + ((unsigned char*)st->inbuffer)[payoffs+1]<<0; + if (paylen < 126) + { + Con_Printf ("%s: payload size encoded badly\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); + goto closesvstream; + } + payoffs += 2; + } + else + { + paylen = ctrl & 0x7f; + } + if (ctrl & 0x80) + { + if (payoffs + 4 > st->inlen) + break; + /*this might read data that isn't set yet, but should be safe*/ + ((unsigned char*)&mask)[0] = ((unsigned char*)st->inbuffer)[payoffs+0]; + ((unsigned char*)&mask)[1] = ((unsigned char*)st->inbuffer)[payoffs+1]; + ((unsigned char*)&mask)[2] = ((unsigned char*)st->inbuffer)[payoffs+2]; + ((unsigned char*)&mask)[3] = ((unsigned char*)st->inbuffer)[payoffs+3]; + payoffs += 4; + } + /*if there isn't space, try again next time around*/ + if (payoffs + paylen > st->inlen) + break; + + if (mask) + { + int i; + for (i = 0; i < paylen; i++) + { + ((unsigned char*)st->inbuffer)[i + payoffs] ^= ((unsigned char*)&mask)[i&3]; + } + } + + net_message.cursize = 0; + + switch((ctrl>>8) & 0xf) + { + case 0: /*continuation*/ + Con_Printf ("websocket continuation frame from %s\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); + goto closesvstream; + case 1: /*text frame*/ +// Con_Printf ("websocket text frame from %s\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); + { + /*text frames are pure utf-8 chars, no dodgy encodings or anything, all pre-checked... + except we're trying to send binary data. + so we need to unmask things (char 0 is encoded as 0x100 - truncate it) + */ + unsigned char *in = st->inbuffer+payoffs, *out = net_message_buffer; + int len = paylen; + while(len && out < net_message_buffer + sizeof(net_message_buffer)) + { + if ((*in & 0xe0)==0xc0 && len > 1) + { + *out = ((in[0] & 0x1f)<<6) | ((in[1] & 0x3f)<<0); + in+=2; + len -= 2; + } + else if (*in & 0x80) + { + *out = '?'; + in++; + len -= 1; + } + else + { + *out = in[0]; + in++; + len -= 1; + } + out++; + } + net_message.cursize = out - net_message_buffer; + } + break; + case 2: /*binary frame*/ + Con_Printf ("websocket binary frame from %s\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); + net_message.cursize = paylen; + if (net_message.cursize >= sizeof(net_message_buffer) ) + { + Con_TPrintf (TL_OVERSIZEPACKETFROM, NET_AdrToString (adr, sizeof(adr), net_from)); + goto closesvstream; + } + memcpy(net_message_buffer, st->inbuffer+payoffs, paylen); + break; + case 8: /*connection close*/ + Con_Printf ("websocket closure %s\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); + goto closesvstream; + case 9: /*ping*/ + Con_Printf ("websocket ping from %s\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); + goto closesvstream; + case 10: /*pong*/ + Con_Printf ("websocket pong from %s\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); + goto closesvstream; + default: + Con_Printf ("Unsupported websocket opcode (%i) from %s\n", (ctrl>>8) & 0xf, NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); + goto closesvstream; + } + +// memcpy(net_message_buffer, st->inbuffer+2, net_message.cursize); + memmove(st->inbuffer, st->inbuffer+payoffs + paylen, st->inlen - (payoffs + paylen)); + st->inlen -= payoffs + paylen; + + if (net_message.cursize) + { + net_message.packing = SZ_RAWBYTES; + net_message.currentbit = 0; + net_from = st->remoteaddr; + return true; + } + } + break; } - - if (st->inlen < 2) - continue; - - net_message.cursize = BigShort(*(short*)st->inbuffer); - if (net_message.cursize >= sizeof(net_message_buffer) ) - { - Con_TPrintf (TL_OVERSIZEPACKETFROM, NET_AdrToString (adr, sizeof(adr), net_from)); - goto closesvstream; - } - if (net_message.cursize+2 > st->inlen) - { //not enough buffered to read a packet out of it. - continue; - } - - memcpy(net_message_buffer, st->inbuffer+2, net_message.cursize); - memmove(st->inbuffer, st->inbuffer+net_message.cursize+2, st->inlen - (net_message.cursize+2)); - st->inlen -= net_message.cursize+2; - - net_message.packing = SZ_RAWBYTES; - net_message.currentbit = 0; - net_from = st->remoteaddr; - - return true; } if (con->generic.thesocket != INVALID_SOCKET && con->active < 256) @@ -2109,7 +2610,7 @@ closesvstream: con->active++; st = Z_Malloc(sizeof(*con->tcpstreams)); - st->waitingforprotocolconfirmation = true; + st->clienttype = TCPC_UNKNOWN; st->next = con->tcpstreams; con->tcpstreams = st; st->socketnum = newsock; @@ -2123,9 +2624,6 @@ closesvstream: else if (st->remoteaddr.type == NA_IPV6) st->remoteaddr.type = NA_TCPV6; - //send the qizmo greeting. - send(newsock, "qizmo\n", 6, 0); - st->timeouttime = timeval + 30; } } @@ -2144,10 +2642,80 @@ qboolean FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int len if (NET_CompareAdr(to, st->remoteaddr)) { - unsigned short slen = BigShort((unsigned short)length); -#pragma warningmsg("TCPConnect: these calls can fail, corrupting the message stream") - send(st->socketnum, (char*)&slen, sizeof(slen), 0); - send(st->socketnum, data, length, 0); + if (!st->outlen) + { + switch(st->clienttype) + { + case TCPC_QIZMO: + { + unsigned short slen = BigShort((unsigned short)length); + #pragma warningmsg("TCPConnect: these calls can fail half way through the write, corrupting the message stream") + send(st->socketnum, (char*)&slen, sizeof(slen), 0); + send(st->socketnum, data, length, 0); + } + break; + case TCPC_WEBSOCKET: + { + /*as a server, we don't need the mask stuff*/ + unsigned short ctrl = 0x8100; + unsigned int paylen = 0; + unsigned int payoffs = 2; + int i; + for (i = 0; i < length; i++) + { + paylen += (((char*)data)[i] == 0 || ((unsigned char*)data)[i] >= 0x80)?2:1; + } + if (paylen >= 126) + { + ctrl |= 126; + payoffs += 2; + } + else + ctrl |= paylen; + + st->outbuffer[0] = ctrl>>8; + st->outbuffer[1] = ctrl&0xff; + if (paylen >= 126) + { + st->outbuffer[2] = paylen>>8; + st->outbuffer[3] = paylen&0xff; + } + /*utf8ify the data*/ + for (i = 0; i < length; i++) + { + if (!((unsigned char*)data)[i]) + { /*0 is encoded as 0x100 to avoid safety checks*/ + st->outbuffer[payoffs++] = 0xc0 | (0x100>>6); + st->outbuffer[payoffs++] = 0x80 | (0x100&0x3f); + } + else if (((unsigned char*)data)[i] >= 0x80) + { /*larger bytes require markup*/ + st->outbuffer[payoffs++] = 0xc0 | (((unsigned char*)data)[i]>>6); + st->outbuffer[payoffs++] = 0x80 | (((unsigned char*)data)[i]&0x3f); + } + else + { /*lower 7 bits are as-is*/ + st->outbuffer[payoffs++] = ((char*)data)[i]; + } + } + st->outlen = payoffs; + } + break; + default: + break; + } + } + + if (st->outlen) + { /*try and flush the old data*/ + int done; + done = send(st->socketnum, st->outbuffer, st->outlen, 0); + if (done > 0) + { + memmove(st->outbuffer, st->outbuffer + done, st->outlen - done); + st->outlen -= done; + } + } st->timeouttime = Sys_DoubleTime() + 20; @@ -2177,6 +2745,29 @@ void FTENET_TCPConnect_Close(ftenet_generic_connection_t *gcon) FTENET_Generic_Close(gcon); } +int FTENET_TCPConnect_SetReceiveFDSet(ftenet_generic_connection_t *gcon, fd_set *fdset) +{ + int maxfd = 0; + ftenet_tcpconnect_connection_t *con = (ftenet_tcpconnect_connection_t*)gcon; + ftenet_tcpconnect_stream_t *st; + + for (st = con->tcpstreams; st; st = st->next) + { + if (st->socketnum == INVALID_SOCKET) + continue; + FD_SET(st->socketnum, fdset); // network socket + if (maxfd < st->socketnum) + maxfd = st->socketnum; + } + if (con->generic.thesocket != INVALID_SOCKET) + { + FD_SET(con->generic.thesocket, fdset); // network socket + if (maxfd < con->generic.thesocket) + maxfd = con->generic.thesocket; + } + return maxfd; +} + ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(int affamily, qboolean isserver, const char *address) { //this is written to support either ipv4 or ipv6, depending on the remote addr. @@ -2244,6 +2835,7 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(int affamily, newcon->generic.GetPacket = FTENET_TCPConnect_GetPacket; newcon->generic.SendPacket = FTENET_TCPConnect_SendPacket; newcon->generic.Close = FTENET_TCPConnect_Close; + newcon->generic.SetReceiveFDSet = FTENET_TCPConnect_SetReceiveFDSet; newcon->generic.islisten = isserver; newcon->generic.addrtype[0] = adr.type; @@ -2257,7 +2849,7 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(int affamily, newcon->active++; newcon->tcpstreams = Z_Malloc(sizeof(*newcon->tcpstreams)); - newcon->tcpstreams->waitingforprotocolconfirmation = true; + newcon->tcpstreams->clienttype = TCPC_UNKNOWN; newcon->tcpstreams->next = NULL; newcon->tcpstreams->socketnum = newsocket; newcon->tcpstreams->inlen = 0; @@ -2875,6 +3467,222 @@ struct ftenet_generic_connection_s *FTENET_IRCConnect_EstablishConnection(qboole #endif +#ifdef HAVE_WEBSOCKCL +#include +#include +#include +#include +#include +#include +extern PPB_Core *ppb_core; +extern PPB_WebSocket *ppb_websocket_interface; +extern PPB_Var *ppb_var_interface; +extern PP_Instance pp_instance; + +typedef struct +{ + ftenet_generic_connection_t generic; + + PP_Resource sock; + netadr_t remoteadr; + + qboolean havepacket; + struct PP_Var incomingpacket; + + qboolean failed; +} ftenet_websocket_connection_t; + +static void websocketgot(void *user_data, int32_t result) +{ + ftenet_websocket_connection_t *wsc = user_data; + if (result == PP_OK) + { + wsc->havepacket = true; + } + else + { + Sys_Printf("%s: %i\n", __func__, result); + wsc->failed = true; + } +} +static void websocketconnected(void *user_data, int32_t result) +{ + ftenet_websocket_connection_t *wsc = user_data; + if (result == PP_OK) + { + int res; + //we got a successful connection, enable reception. + struct PP_CompletionCallback ccb = {websocketgot, wsc, PP_COMPLETIONCALLBACK_FLAG_OPTIONAL}; + res = ppb_websocket_interface->ReceiveMessage(wsc->sock, &wsc->incomingpacket, ccb); + if (res != PP_OK_COMPLETIONPENDING) + websocketgot(wsc, res); + } + else + { + Sys_Printf("%s: %i\n", __func__, result); + //some sort of error connecting, make it timeout now + wsc->failed = true; + } +} +static void websocketclosed(void *user_data, int32_t result) +{ + ftenet_websocket_connection_t *wsc = user_data; + if (wsc->havepacket) + { + wsc->havepacket = false; + ppb_var_interface->Release(wsc->incomingpacket); + } + ppb_core->ReleaseResource(wsc->sock); +// Z_Free(wsc); +} + +static void FTENET_WebSocket_Close(ftenet_generic_connection_t *gcon) +{ + int res; + /*meant to free the memory too, in this case we get the callback to do it*/ + ftenet_websocket_connection_t *wsc = (void*)gcon; + + struct PP_CompletionCallback ccb = {websocketclosed, wsc, PP_COMPLETIONCALLBACK_FLAG_NONE}; + ppb_websocket_interface->Close(wsc->sock, PP_WEBSOCKETSTATUSCODE_NORMAL_CLOSURE, PP_MakeUndefined(), ccb); +} + +static qboolean FTENET_WebSocket_GetPacket(ftenet_generic_connection_t *gcon) +{ + ftenet_websocket_connection_t *wsc = (void*)gcon; + int res; + int len = 0; + if (wsc->havepacket) + { + unsigned char *utf8 = (unsigned char *)ppb_var_interface->VarToUtf8(wsc->incomingpacket, &len); + unsigned char *out = (unsigned char *)net_message_buffer; + + wsc->havepacket = false; + memcpy(&net_from, &wsc->remoteadr, sizeof(net_from)); + + while(len && out < net_message_buffer + sizeof(net_message_buffer)) + { + if ((*utf8 & 0xe0)==0xc0 && len > 1) + { + *out = ((utf8[0] & 0x1f)<<6) | ((utf8[1] & 0x3f)<<0); + utf8+=2; + len -= 2; + } + else if (*utf8 & 0x80) + { + *out = '?'; + utf8++; + len -= 1; + } + else + { + *out = utf8[0]; + utf8++; + len -= 1; + } + out++; + } + net_message.cursize = out - net_message_buffer; + + ppb_var_interface->Release(wsc->incomingpacket); + + if (!wsc->failed) + { + //get the next one + struct PP_CompletionCallback ccb = {websocketgot, wsc, PP_COMPLETIONCALLBACK_FLAG_OPTIONAL}; + res = ppb_websocket_interface->ReceiveMessage(wsc->sock, &wsc->incomingpacket, ccb); + if (res != PP_OK_COMPLETIONPENDING) + websocketgot(wsc, res); + } + + if (len) + { + char adr[64]; + Con_TPrintf (TL_OVERSIZEPACKETFROM, NET_AdrToString (adr, sizeof(adr), net_from)); + return false; + } + return true; + } + return false; +} +static qboolean FTENET_WebSocket_SendPacket(ftenet_generic_connection_t *gcon, int length, void *data, netadr_t to) +{ + ftenet_websocket_connection_t *wsc = (void*)gcon; + int res; + int outchars = 0; + unsigned char outdata[length*2+1]; + unsigned char *out=outdata, *in=data; + if (wsc->failed) + return false; + + while(length-->0) + { + /*FIXME: do we need this code?*/ + if (!*in) + { + *out++ = 0xc0 | (0x100 >> 6); + *out++ = 0x80 | (0x100 & 0x3f); + } + else if (*in >= 0x80) + { + *out++ = 0xc0 | (*in >> 6); + *out++ = 0x80 | (*in & 0x3f); + } + else + *out++ = *in; + in++; + outchars++; + } + *out = 0; + struct PP_Var str = ppb_var_interface->VarFromUtf8(outdata, out - outdata); + res = ppb_websocket_interface->SendMessage(wsc->sock, str); +// Sys_Printf("FTENET_WebSocket_SendPacket: result %i\n", res); + ppb_var_interface->Release(str); + return true; +} + +/*nacl websockets implementation...*/ +static ftenet_generic_connection_t *FTENET_WebSocket_EstablishConnection(qboolean isserver, const char *address) +{ + ftenet_websocket_connection_t *newcon; + + netadr_t adr; + PP_Resource newsocket; + + if (isserver || !ppb_websocket_interface) + { + return NULL; + } + if (!NET_StringToAdr(address, &adr)) + return NULL; //couldn't resolve the name + newcon = Z_Malloc(sizeof(*newcon)); + if (newcon) + { + struct PP_CompletionCallback ccb = {websocketconnected, newcon, PP_COMPLETIONCALLBACK_FLAG_NONE}; + newsocket = ppb_websocket_interface->Create(pp_instance); + struct PP_Var str = ppb_var_interface->VarFromUtf8(adr.address.websocketurl, strlen(adr.address.websocketurl)); + ppb_websocket_interface->Connect(newsocket, str, NULL, 0, ccb); + ppb_var_interface->Release(str); + newcon->generic.name = "WebSocket"; + newcon->generic.GetPacket = FTENET_WebSocket_GetPacket; + newcon->generic.SendPacket = FTENET_WebSocket_SendPacket; + newcon->generic.Close = FTENET_WebSocket_Close; + + newcon->generic.islisten = isserver; + newcon->generic.addrtype[0] = NA_WEBSOCKET; + newcon->generic.addrtype[1] = NA_INVALID; + + newcon->generic.thesocket = INVALID_SOCKET; + newcon->sock = newsocket; + + newcon->remoteadr = adr; + + return &newcon->generic; + } + return NULL; +} +#endif + + /*firstsock is a cookie*/ int NET_GetPacket (netsrc_t netsrc, int firstsock) { @@ -2986,16 +3794,23 @@ void NET_SendPacket (netsrc_t netsrc, int length, void *data, netadr_t to) return; } - Con_Printf("No route to %s - open some ports\n", NET_AdrToString(buffer, sizeof(buffer), to)); + Con_Printf("No route to %s - try reconnecting\n", NET_AdrToString(buffer, sizeof(buffer), to)); } qboolean NET_EnsureRoute(ftenet_connections_t *collection, char *routename, char *host, qboolean islisten) { netadr_t adr; + NET_StringToAdr(host, &adr); switch(adr.type) { +#ifdef HAVE_WEBSOCKCL + case NA_WEBSOCKET: + if (!FTENET_AddToCollection(collection, routename, host, FTENET_WebSocket_EstablishConnection, islisten)) + return false; + break; +#endif #ifdef TCPCONNECT case NA_TCP: if (!FTENET_AddToCollection(collection, routename, host, FTENET_TCP4Connect_EstablishConnection, islisten)) @@ -3051,6 +3866,9 @@ void NET_PrintAddresses(ftenet_connections_t *collection) int TCP_OpenStream (netadr_t remoteaddr) { +#ifndef HAVE_TCP + return INVALID_SOCKET; +#else unsigned long _true = true; int newsocket; int temp; @@ -3077,10 +3895,14 @@ int TCP_OpenStream (netadr_t remoteaddr) return newsocket; +#endif } int TCP_OpenListenSocket (int port) { +#ifndef HAVE_TCP + return INVALID_SOCKET; +#else int newsocket; struct sockaddr_in address; unsigned long _true = true; @@ -3137,10 +3959,11 @@ int maxport = port + 100; } return newsocket; +#endif } - +#if defined(SV_MASTER) || defined(CL_MASTER) int UDP_OpenSocket (int port, qboolean bcast) { SOCKET newsocket; @@ -3338,6 +4161,7 @@ void IPX_CloseSocket (int socket) closesocket(socket); #endif } +#endif // sleeps msec or until net socket is ready //stdin can sometimes be a socket. As a result, @@ -3345,6 +4169,7 @@ void IPX_CloseSocket (int socket) #ifndef CLIENTONLY qboolean NET_Sleep(int msec, qboolean stdinissocket) { +#ifdef HAVE_PACKET struct timeval timeout; fd_set fdset; int maxfd; @@ -3361,13 +4186,22 @@ qboolean NET_Sleep(int msec, qboolean stdinissocket) { if (!svs.sockets->conn[con]) continue; - sock = svs.sockets->conn[con]->thesocket; - if (sock != INVALID_SOCKET) + if (svs.sockets->conn[con]->SetReceiveFDSet) { - FD_SET(sock, &fdset); // network socket + sock = svs.sockets->conn[con]->SetReceiveFDSet(svs.sockets->conn[con], &fdset); if (sock > maxfd) maxfd = sock; } + else + { + sock = svs.sockets->conn[con]->thesocket; + if (sock != INVALID_SOCKET) + { + FD_SET(sock, &fdset); // network socket + if (sock > maxfd) + maxfd = sock; + } + } } timeout.tv_sec = msec/1000; @@ -3376,12 +4210,16 @@ qboolean NET_Sleep(int msec, qboolean stdinissocket) if (stdinissocket) return FD_ISSET(0, &fdset); +#endif return true; } #endif void NET_GetLocalAddress (int socket, netadr_t *out) { +#ifndef HAVE_PACKET + out->type = NA_INVALID; +#else char buff[512]; char adrbuf[MAX_ADR_SIZE]; struct sockaddr_qstorage address; @@ -3416,6 +4254,7 @@ void NET_GetLocalAddress (int socket, netadr_t *out) Con_Printf("Couldn't detect local ip\n"); else Con_TPrintf(TL_IPADDRESSIS, NET_AdrToString (adrbuf, sizeof(adrbuf), *out) ); +#endif } #ifndef CLIENTONLY @@ -3433,13 +4272,19 @@ void SVNET_AddPort_f(void) #endif } +#ifdef HAVE_IPV4 NET_PortToAdr(AF_INET, s, &adr); +#else + adr.type = NA_INVALID; +#endif switch(adr.type) { +#ifdef HAVE_IPV4 case NA_IP: FTENET_AddToCollection(svs.sockets, NULL, s, FTENET_UDP4_EstablishConnection, true); break; +#endif #ifdef IPPROTO_IPV6 case NA_IPV6: FTENET_AddToCollection(svs.sockets, NULL, s, FTENET_UDP6_EstablishConnection, true); @@ -3541,7 +4386,9 @@ void NET_InitClient(void) #ifndef CLIENTONLY FTENET_AddToCollection(cls.sockets, "CLLoopback", port, FTENET_Loop_EstablishConnection, false); #endif +#ifdef HAVE_IPV4 FTENET_AddToCollection(cls.sockets, "CLUDP4", port, FTENET_UDP4_EstablishConnection, true); +#endif #ifdef IPPROTO_IPV6 FTENET_AddToCollection(cls.sockets, "CLUDP6", port, FTENET_UDP6_EstablishConnection, true); #endif @@ -3563,10 +4410,12 @@ void NET_InitClient(void) #ifndef CLIENTONLY +#ifdef HAVE_IPV4 void SV_Tcpport_Callback(struct cvar_s *var, char *oldvalue) { FTENET_AddToCollection(svs.sockets, "SVTCP4", var->string, FTENET_TCP4Connect_EstablishConnection, true); } +#endif #ifdef IPPROTO_IPV6 void SV_Tcpport6_Callback(struct cvar_s *var, char *oldvalue) { @@ -3574,10 +4423,12 @@ void SV_Tcpport6_Callback(struct cvar_s *var, char *oldvalue) } #endif +#ifdef HAVE_IPV4 void SV_Port_Callback(struct cvar_s *var, char *oldvalue) { FTENET_AddToCollection(svs.sockets, "SVUDP4", var->string, FTENET_UDP4_EstablishConnection, true); } +#endif #ifdef IPPROTO_IPV6 void SV_PortIPv6_Callback(struct cvar_s *var, char *oldvalue) { @@ -3617,7 +4468,9 @@ void NET_InitServer(void) allowconnects = true; +#ifdef HAVE_IPV4 Cvar_ForceCallback(&sv_port_ipv4); +#endif #ifdef IPPROTO_IPV6 Cvar_ForceCallback(&sv_port_ipv6); #endif @@ -3678,8 +4531,7 @@ void NET_Shutdown (void) - - +#ifdef HAVE_TCP typedef struct { vfsfile_t funcs; @@ -3807,3 +4659,10 @@ vfsfile_t *FS_OpenTCP(const char *name) else return NULL; } +#else +vfsfile_t *FS_OpenTCP(const char *name) +{ + return NULL; +} +#endif + diff --git a/engine/common/netinc.h b/engine/common/netinc.h index 6891efd19..68f2cc7e9 100644 --- a/engine/common/netinc.h +++ b/engine/common/netinc.h @@ -1,6 +1,36 @@ -#ifdef _WIN32 +#ifndef NACL +#define HAVE_IPV4 //says we can set and receive AF_INET ipv4 udp packets. +#define HAVE_TCP //says we can use tcp too (either ipv4 or ipv6) +#define HAVE_PACKET //if we have the socket api at all... +#endif +#ifdef NACL + + struct sockaddr + { + short sa_family; + }; +/* struct sockaddr_in + { + short sin_family; + unsigned short sin_port; + in_addr sin_addr; + };*/ + #define AF_UNSPEC 0 +// #define AF_INET 1 + + /*NaCl engines cannot host servers. Regular FTE servers can use the same listening tcpconnect socket to host a websocket connection*/ + + #define AF_WEBSOCK 342 + + struct sockaddr_websocket + { + short sws_family; + char url[64]; + }; + +#elif defined(_WIN32) #ifdef _MSC_VER #define USEIPX #endif diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 61ccf539a..e94d41a52 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -778,6 +778,7 @@ int Plug_NewStreamHandle(plugstream_e type) return i; } +#ifndef NACL //EBUILTIN(int, NET_TCPListen, (char *ip, int port, int maxcount)); //returns a new socket with listen enabled. qintptr_t VARGS Plug_Net_TCPListen(void *offset, quintptr_t mask, const qintptr_t *arg) @@ -1030,6 +1031,7 @@ qintptr_t VARGS Plug_Net_SetTLSClient(void *offset, unsigned int mask, const qin return 0; } #endif +#endif qintptr_t VARGS Plug_FS_Open(void *offset, quintptr_t mask, const qintptr_t *arg) { @@ -1170,6 +1172,7 @@ qintptr_t VARGS Plug_Net_Recv(void *offset, quintptr_t mask, const qintptr_t *ar return -2; switch(pluginstreamarray[handle].type) { +#ifndef NACL case STREAM_SOCKET: read = recv(pluginstreamarray[handle].socket, dest, destlen, 0); if (read < 0) @@ -1182,6 +1185,8 @@ qintptr_t VARGS Plug_Net_Recv(void *offset, quintptr_t mask, const qintptr_t *ar else if (read == 0) return -2; //closed by remote connection. return read; +#endif + #ifdef GNUTLS case STREAM_TLS: read = gnutls_record_recv(pluginstreamarray[handle].session, dest, destlen); @@ -1223,6 +1228,7 @@ qintptr_t VARGS Plug_Net_Send(void *offset, quintptr_t mask, const qintptr_t *ar return -2; switch(pluginstreamarray[handle].type) { +#ifndef NACL case STREAM_SOCKET: written = send(pluginstreamarray[handle].socket, src, srclen, 0); if (written < 0) @@ -1235,6 +1241,8 @@ qintptr_t VARGS Plug_Net_Send(void *offset, quintptr_t mask, const qintptr_t *ar else if (written == 0) return -2; //closed by remote connection. return written; +#endif + #ifdef GNUTLS case STREAM_TLS: written = gnutls_record_send(pluginstreamarray[handle].session, src, srclen); @@ -1292,6 +1300,7 @@ qintptr_t VARGS Plug_Net_SendTo(void *offset, quintptr_t mask, const qintptr_t * return -2; switch(pluginstreamarray[handle].type) { +#ifndef NACL case STREAM_SOCKET: written = sendto(pluginstreamarray[handle].socket, src, srclen, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); if (written < 0) @@ -1304,6 +1313,7 @@ qintptr_t VARGS Plug_Net_SendTo(void *offset, quintptr_t mask, const qintptr_t * else if (written == 0) return -2; //closed by remote connection. return written; +#endif default: return -2; } @@ -1327,7 +1337,9 @@ void Plug_Net_Close_Internal(int handle) case STREAM_OSFILE: break; case STREAM_SOCKET: +#ifndef NACL closesocket(pluginstreamarray[handle].socket); +#endif break; case STREAM_TLS: #ifdef GNUTLS @@ -1451,6 +1463,7 @@ void Plug_Init(void) Plug_RegisterBuiltin("Cvar_GetString", Plug_Cvar_GetString, 0); Plug_RegisterBuiltin("Cvar_GetFloat", Plug_Cvar_GetFloat, 0); +#ifndef NACL Plug_RegisterBuiltin("Net_TCPListen", Plug_Net_TCPListen, 0); Plug_RegisterBuiltin("Net_Accept", Plug_Net_Accept, 0); Plug_RegisterBuiltin("Net_TCPConnect", Plug_Net_TCPConnect, 0); @@ -1462,6 +1475,7 @@ void Plug_Init(void) Plug_RegisterBuiltin("Net_Send", Plug_Net_Send, 0); Plug_RegisterBuiltin("Net_SendTo", Plug_Net_SendTo, 0); Plug_RegisterBuiltin("Net_Close", Plug_Net_Close, 0); +#endif Plug_RegisterBuiltin("FS_Open", Plug_FS_Open, 0); Plug_RegisterBuiltin("FS_Read", Plug_Net_Recv, 0); diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 0697d4f9d..25f2f1da7 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -375,19 +375,18 @@ void QCBUILTIN PF_getsurfaceclippedpoint(progfuncs_t *prinst, struct globalvars_ { } +#ifndef TERRAIN void QCBUILTIN PF_terrain_edit(progfuncs_t *prinst, struct globalvars_s *pr_globals) { + G_FLOAT(OFS_RETURN) = false; world_t *w = prinst->parms->user; int action = G_FLOAT(OFS_PARM0); float *pos = G_VECTOR(OFS_PARM1); float radius = G_FLOAT(OFS_PARM2); float quant = G_FLOAT(OFS_PARM3); -#if defined(TERRAIN) G_FLOAT(OFS_RETURN) = Heightmap_Edit(w->worldmodel, action, pos, radius, quant); -#else - G_FLOAT(OFS_RETURN) = false; -#endif } +#endif //end model functions //////////////////////////////////////////////////// @@ -3403,7 +3402,7 @@ nolength: o += strlen(o); break; case 'c': - if(flags & PRINTF_ALTERNATE) +// if(flags & PRINTF_ALTERNATE) { if(precision < 0) // not set Q_snprintfz(o, end - o, formatbuf, width, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); @@ -3411,20 +3410,20 @@ nolength: Q_snprintfz(o, end - o, formatbuf, width, precision, (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg))); o += strlen(o); } - else +/* else { unsigned int c = (isfloat ? (unsigned int) GETARG_FLOAT(thisarg) : (unsigned int) GETARG_INT(thisarg)); -// char charbuf16[16]; -// const char *buf = u8_encodech(c, NULL, charbuf16); -// if(!buf) -// buf = ""; + char charbuf16[16]; + const char *buf = u8_encodech(c, NULL, charbuf16); + if(!buf) + buf = ""; if(precision < 0) // not set precision = end - o - 1; -// o += u8_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision); + o += u8_strpad(o, end - o, buf, (flags & PRINTF_LEFT) != 0, width, precision); } - break; +*/ break; case 's': - if(flags & PRINTF_ALTERNATE) +// if(flags & PRINTF_ALTERNATE) { if(precision < 0) // not set Q_snprintfz(o, end - o, formatbuf, width, GETARG_STRING(thisarg)); @@ -3432,13 +3431,13 @@ nolength: Q_snprintfz(o, end - o, formatbuf, width, precision, GETARG_STRING(thisarg)); o += strlen(o); } - else +/* else { if(precision < 0) // not set precision = end - o - 1; -// o += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision); + o += u8_strpad(o, end - o, GETARG_STRING(thisarg), (flags & PRINTF_LEFT) != 0, width, precision); } - break; +*/ break; default: Con_Printf("PF_sprintf: invalid format string: %s\n", s0); goto finished; diff --git a/engine/common/protocol.h b/engine/common/protocol.h index e78a9f233..1bee944c9 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -1014,6 +1014,7 @@ typedef struct q1usercmd_s #define Q2RF_ADDITIVE 0x00080000 #define RF_NOSHADOW 0x00100000 #define RF_NODEPTHTEST 0x00200000 +#define RF_FORCECOLOURMOD 0x00400000 // player_state_t->refdef flags #define Q2RDF_UNDERWATER 1 // warp the screen as apropriate diff --git a/engine/common/sha1.c b/engine/common/sha1.c new file mode 100644 index 000000000..e20941d11 --- /dev/null +++ b/engine/common/sha1.c @@ -0,0 +1,190 @@ +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +Test Vectors (from FIPS PUB 180-1) +"abc" +A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" +34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + +This file came to FTE via EzQuake. +*/ + +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#include "quakedef.h" + + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +#define blk0(i) (block->l[i] = BigLong(block->l[i])) + +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ +^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + +typedef struct +{ + unsigned int state[5]; + unsigned int count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +#define DIGEST_SIZE 20 +void SHA1Transform(unsigned int state[5], unsigned char buffer[64]); +void SHA1Init(SHA1_CTX* context); +void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len); +void SHA1Final(unsigned char digest[DIGEST_SIZE], SHA1_CTX* context); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(unsigned int state[5], unsigned char buffer[64]) +{ + unsigned int a, b, c, d, e; + typedef union + { + unsigned char c[64]; + unsigned int l[16]; + } CHAR64LONG16; + CHAR64LONG16* block; + #ifdef SHA1HANDSOFF + static unsigned char workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); + #else + block = (CHAR64LONG16*)buffer; + #endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) +{ + unsigned int i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) + { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) + { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else + i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[DIGEST_SIZE], SHA1_CTX* context) +{ + unsigned int i, j; + unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) + { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) + { + SHA1Update(context, (unsigned char *)"\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ + for (i = 0; i < DIGEST_SIZE; i++) + { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + i = j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); +memset(&finalcount, 0, 8); +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ + SHA1Transform(context->state, context->buffer); +#endif +} + + +int SHA1(char *digest, int maxdigestsize, char *string) +{ + SHA1_CTX context; + if (maxdigestsize < DIGEST_SIZE) + return 0; + + SHA1Init(&context); + SHA1Update(&context, (unsigned char*) string, strlen(string)); + SHA1Final(digest, &context); + + return DIGEST_SIZE; +} diff --git a/engine/common/world.h b/engine/common/world.h index 5e9b36d20..bc35802e4 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -99,7 +99,7 @@ typedef struct q2trace_s #define FL_ONGROUND (1<<9) #define FL_PARTIALGROUND (1<<10) // not all corners are valid #define FL_WATERJUMP (1<<11) // player jumping out of water -// FL_JUMPRELEASED //12 +#define FL_JUMPRELEASED (1<<12) //13 #define FL_FINDABLE_NONSOLID (1<<14) //a cpqwsv feature #define FL_MOVECHAIN_ANGLE (1<<15) // hexen2 - when in a move chain, will update the angle diff --git a/engine/dotnet2005/ftequake.sln b/engine/dotnet2005/ftequake.sln index c9f641a51..a4a5076c6 100644 --- a/engine/dotnet2005/ftequake.sln +++ b/engine/dotnet2005/ftequake.sln @@ -22,6 +22,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qtvprox", "..\..\fteqtv\dot EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "droid", "droid\droid.vcproj", "{AA9D4AA8-2B98-42A8-9F63-B9E5A6221BB6}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nacl", "..\nacl\nacl.vcproj", "{4735677B-6D5A-4BE6-A945-CB32A7282F56}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution D3DDebug|Win32 = D3DDebug|Win32 @@ -321,6 +323,42 @@ Global {AA9D4AA8-2B98-42A8-9F63-B9E5A6221BB6}.Release Dedicated Server|x64.ActiveCfg = Release|Win32 {AA9D4AA8-2B98-42A8-9F63-B9E5A6221BB6}.Release|Win32.ActiveCfg = Release|Win32 {AA9D4AA8-2B98-42A8-9F63-B9E5A6221BB6}.Release|x64.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.D3DDebug|Win32.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.D3DDebug|Win32.Build.0 = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.D3DDebug|x64.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.D3DRelease|Win32.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.D3DRelease|Win32.Build.0 = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.D3DRelease|x64.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Debug Dedicated Server|Win32.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Debug Dedicated Server|Win32.Build.0 = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Debug Dedicated Server|x64.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Debug|Win32.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Debug|Win32.Build.0 = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Debug|x64.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.GLDebug|Win32.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.GLDebug|Win32.Build.0 = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.GLDebug|x64.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.GLRelease|Win32.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.GLRelease|Win32.Build.0 = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.GLRelease|x64.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MDebug|Win32.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MDebug|Win32.Build.0 = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MDebug|x64.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MinGLDebug|Win32.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MinGLDebug|Win32.Build.0 = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MinGLDebug|x64.ActiveCfg = Debug|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MinGLRelease|Win32.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MinGLRelease|Win32.Build.0 = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MinGLRelease|x64.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MRelease|Win32.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MRelease|Win32.Build.0 = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.MRelease|x64.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Release Dedicated Server|Win32.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Release Dedicated Server|Win32.Build.0 = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Release Dedicated Server|x64.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Release|Win32.ActiveCfg = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Release|Win32.Build.0 = Release|Win32 + {4735677B-6D5A-4BE6-A945-CB32A7282F56}.Release|x64.ActiveCfg = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/engine/dotnet2005/ftequake.vcproj b/engine/dotnet2005/ftequake.vcproj index 32d11b7ed..eeea29d42 100644 --- a/engine/dotnet2005/ftequake.vcproj +++ b/engine/dotnet2005/ftequake.vcproj @@ -1681,7 +1681,7 @@ FavorSizeOrSpeed="1" OmitFramePointers="true" AdditionalIncludeDirectories="../libs/speex,..\client,../libs/freetype2/include,../common,../server,../gl,../sw,../qclib,../libs,../libs/dxsdk7/include" - PreprocessorDefinitions="NDEBUG;GLQUAKE;WIN32;_WINDOWS" + PreprocessorDefinitions="NDEBUG;GLQUAKE;WIN32;_WINDOWS;BOTLIB_STATIC" StringPooling="true" ExceptionHandling="0" BufferSecurityCheck="false" @@ -23218,6 +23218,194 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -25497,194 +25685,6 @@ /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -33057,6 +33057,10 @@ /> + + diff --git a/engine/droid/src/com/fteqw/FTEDroidActivity.java b/engine/droid/src/com/fteqw/FTEDroidActivity.java index e42ca4625..7d3a34a6d 100644 --- a/engine/droid/src/com/fteqw/FTEDroidActivity.java +++ b/engine/droid/src/com/fteqw/FTEDroidActivity.java @@ -33,7 +33,7 @@ public class FTEDroidActivity extends Activity private class FTERenderer implements GLSurfaceView.Renderer { private boolean inited; - private String basedir; + private String basedir, userdir; FTEDroidActivity act; FTERenderer(Context ctx, FTEDroidActivity parent) @@ -48,8 +48,16 @@ public class FTEDroidActivity extends Activity { /*oh well, can just use the homedir instead*/ } +// try +// { + userdir = Environment.getExternalStorageDirectory().getPath(); +// } +// catch(foo) +// { +// } android.util.Log.i("FTEDroid", "Base dir is \"" + basedir + "\"."); + android.util.Log.i("FTEDroid", "User dir is \"" + userdir + "\"."); } @Override @@ -64,7 +72,7 @@ public class FTEDroidActivity extends Activity public void onSurfaceChanged(GL10 gl, int width, int height) { android.util.Log.i("FTEDroid", "Surface changed, now " + width + " by " + height + "."); - FTEDroidEngine.init(width, height, basedir); + FTEDroidEngine.init(width, height, basedir, userdir); inited = true; } @Override diff --git a/engine/droid/src/com/fteqw/FTEDroidEngine.java b/engine/droid/src/com/fteqw/FTEDroidEngine.java index a946c87a8..5ddc0a15c 100644 --- a/engine/droid/src/com/fteqw/FTEDroidEngine.java +++ b/engine/droid/src/com/fteqw/FTEDroidEngine.java @@ -2,7 +2,7 @@ package com.fteqw; public class FTEDroidEngine { - public static native void init(int w, int h, String basedir); /* init/reinit */ + public static native void init(int w, int h, String apkpath, String usrpath); /* init/reinit */ public static native void frame(float ax, float ay, float az); public static native void keypress(int down, int qkey, int unicode); public static native void motion(int act, int pointerid, float x, float y); diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index c7824231b..c94aadf72 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -1021,6 +1021,8 @@ void R_GAlias_GenerateBatches(entity_t *e, batch_t **batches) b->surf_first = surfnum; b->flags = 0; sort = shader->sort; + if (e->flags & RF_FORCECOLOURMOD) + b->flags |= BEF_FORCECOLOURMOD; if (e->flags & Q2RF_ADDITIVE) { b->flags |= BEF_FORCEADDITIVE; @@ -1915,16 +1917,16 @@ void BE_GenModelBatches(batch_t **batches) for (i = 0; i < SHADER_SORT_COUNT; i++) batches[i] = NULL; - if (!r_drawentities.ival) - return; - - Alias_FlushCache(); - #if defined(TERRAIN) if (cl.worldmodel && cl.worldmodel->type == mod_heightmap) GL_DrawHeightmapModel(batches, &r_worldentity); #endif + if (!r_drawentities.ival) + return; + + Alias_FlushCache(); + // draw sprites seperately, because of alpha blending for (i=0 ; itexnum; - } } + if (!glt) + glt = GL_AllocNewGLTexture(identifier, width, height); TRACE(("dbg: GL_LoadTexture: new %s\n", identifier)); - glt = GL_AllocNewGLTexture(identifier, width, height); glt->bpp = 8; glt->flags = flags; -checkglerror(); GL_MTBind(0, GL_TEXTURE_2D, glt->texnum); GL_Upload8 ("8bit", data, width, height, flags, transtype); -checkglerror(); return glt->texnum; } diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 9be1975eb..e27be2586 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -1,49 +1,302 @@ #include "quakedef.h" -#if defined(TERRAIN) && !defined(SERVERONLY) //fixme +#ifdef TERRAIN #ifdef GLQUAKE #include "glquake.h" #endif #include "shader.h" +#include "pr_common.h" + +int Surf_LM_AllocBlock (int w, int h, int *x, int *y, shader_t *shader); + //heightmaps work thusly: //there is one raw heightmap file //the file is split to 4*4 sections. -//each section is textured independantly (remember banshees are capped at 512 pixels) -//there's a detailtexture blended over the top to fake the detail. +//each section is textured independantly (remember banshees are capped at 256*256 pixels) //it's built into 16 seperate display lists, these display lists are individually culled, but the drivers are expected to optimise them too. -//Tei claims 14x speedup with a single display list. hopefully we can achieve the same speed by culling per-texture. +//Tei claims 14x speedup with a single display list. hopefully we can achieve the same speed by culling per-section. //we get 20->130 //perhaps we should build it with multitexture? (no - slower on ati) -#define SECTIONS 8 +#define MAXSECTIONS 64 //this many sections max in each direction +#define SECTTEXSIZE 64 //this many texture samples per section +#define SECTHEIGHTSIZE 16 //this many height samples per section +typedef struct +{ + char texname[4][32]; + unsigned int texmap[SECTTEXSIZE][SECTTEXSIZE]; + float heights[SECTHEIGHTSIZE*SECTHEIGHTSIZE]; + unsigned short holes; +} dsection_t; +typedef struct +{ + float heights[SECTHEIGHTSIZE*SECTHEIGHTSIZE]; + unsigned short holes; +#ifndef SERVERONLY + char texname[4][32]; + int lightmap; + int lmx, lmy; + + texnums_t textures; + vbo_t vbo; + unsigned short minh, maxh; + mesh_t mesh; + mesh_t *amesh; + qboolean modified:1; +#endif +} hmsection_t; typedef struct { char path[MAX_QPATH]; - char hmapname[MAX_QPATH]; - unsigned short *heights; - int terrainsize; - float terrainscale; - float heightscale; - int numsegs; - texid_t detailtexture; - texid_t textures[SECTIONS*SECTIONS]; - int displaylist[SECTIONS*SECTIONS]; //display lists are famous for being stupidly fast with heightmaps. - unsigned short mins[SECTIONS*SECTIONS], maxs[SECTIONS*SECTIONS]; - + int numsegsx, numsegsy; //tex/cull sections + float sectionsize; //each section is this big, in world coords + hmsection_t *section[MAXSECTIONS*MAXSECTIONS]; shader_t *skyshader; shader_t *shader; - mesh_t mesh[SECTIONS*SECTIONS]; - mesh_t *amesh[SECTIONS*SECTIONS]; mesh_t skymesh; mesh_t *askymesh; - - qboolean modified[SECTIONS*SECTIONS]; } heightmap_t; -#define DISPLISTS -//#define MULTITEXTURE //ATI suck. I don't know about anyone else (this goes at 1/5th the speed). +static void GL_LoadSectionTextures(hmsection_t *s) +{ +#ifndef SERVERONLY + //CL_CheckOrEnqueDownloadFile(s->texname[0], NULL, 0); + //CL_CheckOrEnqueDownloadFile(s->texname[1], NULL, 0); + //CL_CheckOrEnqueDownloadFile(s->texname[2], NULL, 0); + //CL_CheckOrEnqueDownloadFile(s->texname[3], NULL, 0); + s->textures.base = R_LoadHiResTexture(s->texname[0], NULL, 0); + s->textures.upperoverlay = R_LoadHiResTexture(s->texname[1], NULL, 0); + s->textures.loweroverlay = R_LoadHiResTexture(s->texname[2], NULL, 0); + s->textures.fullbright = R_LoadHiResTexture(s->texname[3], NULL, 0); + s->textures.bump = R_LoadHiResTexture(va("%s_norm", s->texname[0]), NULL, 0); + s->textures.specular = R_LoadHiResTexture(va("%s_spec", s->texname[0]), NULL, 0); +#endif +} +static char *GL_DiskSectionName(heightmap_t *hm, int sx, int sy) +{ + return va("maps/%s/sect_%02i_%02i.hms", hm->path, sx, sy); +} +static hmsection_t *GL_LoadSection(heightmap_t *hm, int sx, int sy) +{ + hmsection_t *s; + dsection_t *ds; + int i; +#ifndef SERVERONLY + unsigned char *lm; +#endif + + s = malloc(sizeof(*s)); + if (!s) + return NULL; + memset(s, 0, sizeof(*s)); + +#ifndef SERVERONLY + s->lightmap = -1; + + Q_strncpyz(s->texname[0], va("maps/%s/grass", hm->path), sizeof(s->texname[0])); + Q_strncpyz(s->texname[1], va("maps/%s/rock", hm->path), sizeof(s->texname[1])); + Q_strncpyz(s->texname[2], va("maps/%s/road", hm->path), sizeof(s->texname[2])); + Q_strncpyz(s->texname[3], va("maps/%s/ground", hm->path), sizeof(s->texname[3])); + s->modified = true; + + if (s->lightmap < 0) + { + s->lightmap = Surf_LM_AllocBlock(SECTTEXSIZE, SECTTEXSIZE, &s->lmx, &s->lmy, hm->shader); + BE_UploadAllLightmaps(); + } +#endif + + if (FS_LoadFile(GL_DiskSectionName(hm, sx, sy), &ds) >= 0) + { +#ifndef SERVERONLY + Q_strncpyz(s->texname[0], ds->texname[0], sizeof(s->texname[0])); + Q_strncpyz(s->texname[1], ds->texname[1], sizeof(s->texname[1])); + Q_strncpyz(s->texname[2], ds->texname[2], sizeof(s->texname[2])); + Q_strncpyz(s->texname[3], ds->texname[3], sizeof(s->texname[3])); + + lm = lightmap[s->lightmap]->lightmaps; + lm += (s->lmx * LMBLOCK_WIDTH + s->lmy) * lightmap_bytes; + for (i = 0; i < SECTTEXSIZE; i++) + { + memcpy(lm, ds->texmap + i, sizeof(ds->texmap[0])); + lm += (LMBLOCK_WIDTH)*lightmap_bytes; + } + lightmap[s->lightmap]->modified = true; + lightmap[s->lightmap]->rectchange.l = 0; + lightmap[s->lightmap]->rectchange.t = 0; + lightmap[s->lightmap]->rectchange.w = LMBLOCK_WIDTH; + lightmap[s->lightmap]->rectchange.h = LMBLOCK_HEIGHT; +#endif + + /*load the heights too*/ + for (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++) + { + s->heights[i] = LittleFloat(ds->heights[i]); + } + + FS_FreeFile(ds); + } + else + { +#if 0//def DEBUG + void *f; + if (lightmap_bytes == 4 && lightmap_bgra && FS_LoadFile(va("maps/%s/splatt.png", hm->path), &f) >= 0) + { + //temp + int vx, vy; + int x, y; + extern qbyte *Read32BitImageFile(qbyte *buf, int len, int *width, int *height, qboolean *hasalpha, char *fname); + int sw, sh; + qboolean hasalpha; + unsigned char *splatter = Read32BitImageFile(f, com_filesize, &sw, &sh, &hasalpha, "splattermap"); + if (splatter) + { + lm = lightmap[s->lightmap]->lightmaps; + lm += (s->lmx * LMBLOCK_WIDTH + s->lmy) * lightmap_bytes; + + for (vx = 0; vx < SECTTEXSIZE; vx++) + { + x = sw * (((float)sy) + ((float)vx / (SECTTEXSIZE-1))) / hm->numsegsx; + if (x > sw-1) + x = sw-1; + for (vy = 0; vy < SECTTEXSIZE; vy++) + { + y = sh * (((float)sx) + ((float)vy / (SECTTEXSIZE-1))) / hm->numsegsy; + if (y > sh-1) + y = sh-1; + + lm[2] = splatter[(y + x*sh)*4+0]; + lm[1] = splatter[(y + x*sh)*4+1]; + lm[0] = splatter[(y + x*sh)*4+2]; + lm[3] = splatter[(y + x*sh)*4+3]; + lm += 4; + } + lm += (LMBLOCK_WIDTH - SECTTEXSIZE)*lightmap_bytes; + } + BZ_Free(splatter); + + lightmap[s->lightmap]->modified = true; + lightmap[s->lightmap]->rectchange.l = 0; + lightmap[s->lightmap]->rectchange.t = 0; + lightmap[s->lightmap]->rectchange.w = LMBLOCK_WIDTH; + lightmap[s->lightmap]->rectchange.h = LMBLOCK_HEIGHT; + } + FS_FreeFile(f); + } + + if (lightmap_bytes == 4 && lightmap_bgra && FS_LoadFile(va("maps/%s/heightmap.png", hm->path), &f) >= 0) + { + //temp + int vx, vy; + int x, y; + extern qbyte *Read32BitImageFile(qbyte *buf, int len, int *width, int *height, qboolean *hasalpha, char *fname); + int sw, sh; + float *h; + qboolean hasalpha; + unsigned char *hmimage = Read32BitImageFile(f, com_filesize, &sw, &sh, &hasalpha, "heightmap"); + if (hmimage) + { + h = s->heights; + + for (vx = 0; vx < SECTHEIGHTSIZE; vx++) + { + x = sw * (((float)sy) + ((float)vx / (SECTHEIGHTSIZE-1))) / hm->numsegsx; + if (x > sw-1) + x = sw-1; + for (vy = 0; vy < SECTHEIGHTSIZE; vy++) + { + y = sh * (((float)sx) + ((float)vy / (SECTHEIGHTSIZE-1))) / hm->numsegsy; + if (y > sh-1) + y = sh-1; + + *h = 0; + *h += hmimage[(y + x*sh)*4+0]; + *h += hmimage[(y + x*sh)*4+1]<<8; + *h += hmimage[(y + x*sh)*4+2]<<16; + *h *= 4.0f/(1<<16); + h++; + } + } + BZ_Free(hmimage); + } + FS_FreeFile(f); + } +#endif + } + + GL_LoadSectionTextures(s); + + hm->section[sx+sy*MAXSECTIONS] = s; + + return s; +} + +static void GL_SaveSection(heightmap_t *hm, int sx, int sy) +{ +#ifndef SERVERONLY + hmsection_t *s = hm->section[sx+sy*MAXSECTIONS]; + dsection_t ds; + unsigned char *lm; + int i; + if (!s || s->lightmap < 0) + return; + + Q_strncpyz(ds.texname[0], s->texname[0], sizeof(ds.texname[0])); + Q_strncpyz(ds.texname[1], s->texname[1], sizeof(ds.texname[1])); + Q_strncpyz(ds.texname[2], s->texname[2], sizeof(ds.texname[2])); + Q_strncpyz(ds.texname[3], s->texname[3], sizeof(ds.texname[3])); + + lm = lightmap[s->lightmap]->lightmaps; + lm += (s->lmx * LMBLOCK_WIDTH + s->lmy) * lightmap_bytes; + for (i = 0; i < SECTTEXSIZE; i++) + { + memcpy(ds.texmap + i, lm, sizeof(ds.texmap[0])); + lm += (LMBLOCK_WIDTH)*lightmap_bytes; + } + + for (i = 0; i < SECTHEIGHTSIZE*SECTHEIGHTSIZE; i++) + { + ds.heights[i] = LittleFloat(s->heights[i]); + } + + FS_WriteFile(GL_DiskSectionName(hm, sx, sy), &ds, sizeof(ds), FS_GAMEONLY); +#endif +} + +/*save all currently loaded sections*/ +void HeightMap_Save(heightmap_t *hm) +{ + hmsection_t *s; + int x, y; + for (x = 0; x < hm->numsegsx; x++) + { + for (y = 0; y < hm->numsegsy; y++) + { + s = hm->section[x+y*MAXSECTIONS]; + GL_SaveSection(hm, x, y); + } + } +} + +/*purge all sections*/ +void HeightMap_Purge(model_t *mod) +{ + heightmap_t *hm = mod->terrain; + hmsection_t *s; + int x, y; + for (x = 0; x < hm->numsegsx; x++) + { + for (y = 0; y < hm->numsegsy; y++) + { + s = hm->section[x+y*MAXSECTIONS]; + hm->section[x+y*MAXSECTIONS] = NULL; + free(s); + } + } +} +#ifndef SERVERONLY void GL_DrawHeightmapModel (batch_t **batches, entity_t *e) { //a 512*512 heightmap @@ -52,19 +305,19 @@ void GL_DrawHeightmapModel (batch_t **batches, entity_t *e) //with 130 to 180fps, display lists rule! int x, y, vx, vy, v; - float subsize; - int minx, miny; vec3_t mins, maxs; model_t *m = e->model; heightmap_t *hm = m->terrain; mesh_t *mesh; batch_t *b; + hmsection_t *s; if (e->model == cl.worldmodel) { b = BE_GetTempBatch(); if (b) { + b->lightmap = -1; b->ent = e; b->shader = hm->skyshader; b->flags = 0; @@ -74,7 +327,7 @@ void GL_DrawHeightmapModel (batch_t **batches, entity_t *e) b->buildmeshes = NULL; b->skin = &b->shader->defaulttextures; b->texture = NULL; - // vbo = b->vbo = hm->vbo[x+y*SECTIONS]; + // vbo = b->vbo = hm->vbo[x+y*MAXSECTIONS]; b->vbo = NULL; b->next = batches[b->shader->sort]; @@ -82,83 +335,131 @@ void GL_DrawHeightmapModel (batch_t **batches, entity_t *e) } } - subsize = hm->terrainsize/SECTIONS; - for (x = 0; x < hm->numsegs; x++) + for (x = 0; x < hm->numsegsx; x++) { - mins[0] = (x+0)*subsize*hm->terrainscale; - maxs[0] = (x+1)*subsize*hm->terrainscale; - for (y = 0; y < hm->numsegs; y++) + mins[0] = (x+0)*hm->sectionsize; + maxs[0] = (x+1)*hm->sectionsize; + for (y = 0; y < hm->numsegsy; y++) { - mins[1] = (y+0)*subsize*hm->terrainscale; - maxs[1] = (y+1)*subsize*hm->terrainscale; + mins[1] = (y+0)*hm->sectionsize; + maxs[1] = (y+1)*hm->sectionsize; - mesh = &hm->mesh[x+y*SECTIONS]; - if (hm->modified[x+y*SECTIONS]) + s = hm->section[x+y*MAXSECTIONS]; + if (!s) { - minx = x*subsize; - miny = y*subsize; + s = GL_LoadSection(hm, x, y); + if (!s) + continue; + } + mesh = &s->mesh; + if (s->modified) + { +// minx = x*SECTHEIGHTSIZE; +// miny = y*SECTHEIGHTSIZE; - hm->modified[x+y*SECTIONS] = false; + s->modified = false; - hm->mins[x+y*SECTIONS] = 65536 * hm->heightscale; - hm->maxs[x+y*SECTIONS] = 0; + if (s->lightmap < 0) + { + s->lightmap = Surf_LM_AllocBlock(SECTTEXSIZE, SECTTEXSIZE, &s->lmx, &s->lmy, hm->shader); + BE_UploadAllLightmaps(); + } + + s->minh = 999999999999999; + s->maxh = -999999999999999; if (!mesh->xyz_array) - mesh->xyz_array = BZ_Malloc(sizeof(vecV_t) * (subsize+1)*(subsize+1)); - if (!mesh->st_array) - mesh->st_array = BZ_Malloc(sizeof(vec2_t) * (subsize+1)*(subsize+1)); - if (!mesh->lmst_array) - mesh->lmst_array = BZ_Malloc(sizeof(vec2_t) * (subsize+1)*(subsize+1)); + { + mesh->xyz_array = BZ_Malloc((sizeof(vecV_t)+sizeof(vec2_t)+sizeof(vec2_t)) * (SECTHEIGHTSIZE)*(SECTHEIGHTSIZE)); + mesh->st_array = (void*) (mesh->xyz_array + (SECTHEIGHTSIZE)*(SECTHEIGHTSIZE)); + mesh->lmst_array = (void*) (mesh->st_array + (SECTHEIGHTSIZE)*(SECTHEIGHTSIZE)); + } mesh->numvertexes = 0; /*64 quads across requires 65 verticies*/ - for (vx = 0; vx <= subsize; vx++) + for (vx = 0; vx < SECTHEIGHTSIZE; vx++) { - for (vy = 0; vy <= subsize; vy++) + for (vy = 0; vy < SECTHEIGHTSIZE; vy++) { v = mesh->numvertexes++; - mesh->xyz_array[v][0] = (vx+minx)*hm->terrainscale; - mesh->xyz_array[v][1] = (vy+miny)*hm->terrainscale; - mesh->xyz_array[v][2] = hm->heights[(vx+minx) + (vy+miny)*hm->terrainsize]*hm->heightscale; + mesh->xyz_array[v][0] = (x + vx/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize; + mesh->xyz_array[v][1] = (y + vy/(SECTHEIGHTSIZE-1.0f)) * hm->sectionsize; + mesh->xyz_array[v][2] = s->heights[vx + vy*SECTHEIGHTSIZE]; - if (hm->maxs[x+y*SECTIONS] < mesh->xyz_array[v][2]) - hm->maxs[x+y*SECTIONS] = mesh->xyz_array[v][2]; - if (hm->mins[x+y*SECTIONS] > mesh->xyz_array[v][2]) - hm->mins[x+y*SECTIONS] = mesh->xyz_array[v][2]; + if (s->maxh < mesh->xyz_array[v][2]) + s->maxh = mesh->xyz_array[v][2]; + if (s->minh > mesh->xyz_array[v][2]) + s->minh = mesh->xyz_array[v][2]; mesh->st_array[v][0] = mesh->xyz_array[v][0] / 64; mesh->st_array[v][1] = mesh->xyz_array[v][1] / 64; - mesh->lmst_array[v][0] = mesh->xyz_array[v][0] / 64; - mesh->lmst_array[v][1] = mesh->xyz_array[v][1] / 64; + //calc the position in the range -0.5 to 0.5 + mesh->lmst_array[v][0] = (((float)vx / (SECTHEIGHTSIZE-1))-0.5); + mesh->lmst_array[v][1] = (((float)vy / (SECTHEIGHTSIZE-1))-0.5); + //scale down to a half-texel + mesh->lmst_array[v][0] *= (SECTTEXSIZE-1.0f)/LMBLOCK_WIDTH; + mesh->lmst_array[v][1] *= (SECTTEXSIZE-1.0f)/LMBLOCK_HEIGHT; + //bias it + mesh->lmst_array[v][0] += ((float)SECTTEXSIZE/(LMBLOCK_WIDTH*2)) + ((float)(s->lmy) / LMBLOCK_WIDTH); + mesh->lmst_array[v][1] += ((float)SECTTEXSIZE/(LMBLOCK_HEIGHT*2)) + ((float)(s->lmx) / LMBLOCK_HEIGHT); + + //TODO: include colour tints } } if (!mesh->indexes) - mesh->indexes = BZ_Malloc(sizeof(index_t) * subsize*subsize*6); + mesh->indexes = BZ_Malloc(sizeof(index_t) * SECTHEIGHTSIZE*SECTHEIGHTSIZE*6); mesh->numindexes = 0; - for (vx = 0; vx < subsize; vx++) + for (vx = 0; vx < SECTHEIGHTSIZE-1; vx++) { - for (vy = 0; vy < subsize; vy++) + for (vy = 0; vy < SECTHEIGHTSIZE-1; vy++) { - v = vx + vy*(subsize+1); + //TODO: holes + if (s->holes & (vx / (SECTHEIGHTSIZE/4)) << (vy / (SECTHEIGHTSIZE/4)) ) + continue; + v = vx + vy*(SECTHEIGHTSIZE); mesh->indexes[mesh->numindexes++] = v+0; mesh->indexes[mesh->numindexes++] = v+1; - mesh->indexes[mesh->numindexes++] = v+subsize+1; + mesh->indexes[mesh->numindexes++] = v+SECTHEIGHTSIZE; mesh->indexes[mesh->numindexes++] = v+1; - mesh->indexes[mesh->numindexes++] = v+1+subsize+1; - mesh->indexes[mesh->numindexes++] = v+subsize+1; + mesh->indexes[mesh->numindexes++] = v+1+SECTHEIGHTSIZE; + mesh->indexes[mesh->numindexes++] = v+SECTHEIGHTSIZE; } } + if (qrenderer == QR_OPENGL) + { + if (!s->vbo.coord.gl.vbo) + qglGenBuffersARB(1, &s->vbo.coord.gl.vbo); + s->vbo.coord.gl.addr = 0; + GL_SelectVBO(s->vbo.coord.gl.vbo); + qglBufferDataARB(GL_ARRAY_BUFFER_ARB, (sizeof(vecV_t)+sizeof(vec2_t)+sizeof(vec2_t)) * (mesh->numvertexes), mesh->xyz_array, GL_STATIC_DRAW_ARB); + GL_SelectVBO(0); + s->vbo.texcoord.gl.addr = (void*)((char*)mesh->st_array - (char*)mesh->xyz_array); + s->vbo.texcoord.gl.vbo = s->vbo.coord.gl.vbo; + s->vbo.lmcoord.gl.addr = (void*)((char*)mesh->lmst_array - (char*)mesh->xyz_array); + s->vbo.lmcoord.gl.vbo = s->vbo.coord.gl.vbo; +// Z_Free(mesh->xyz_array); +// mesh->xyz_array = NULL; +// mesh->st_array = NULL; +// mesh->lmst_array = NULL; - //GL_BuildVBO(); + if (!s->vbo.indicies.gl.vbo) + qglGenBuffersARB(1, &s->vbo.indicies.gl.vbo); + s->vbo.indicies.gl.addr = 0; + GL_SelectEBO(s->vbo.indicies.gl.vbo); + qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(index_t) * mesh->numindexes, mesh->indexes, GL_STATIC_DRAW_ARB); + GL_SelectEBO(0); +// Z_Free(mesh->indexes); +// mesh->indexes = NULL; + } } - mins[2] = hm->mins[x+y*SECTIONS]; - maxs[2] = hm->maxs[x+y*SECTIONS]; + mins[2] = s->minh; + maxs[2] = s->maxh; - if (!BoundsIntersect(mins, maxs, r_refdef.vieworg, r_refdef.vieworg)) +// if (!BoundsIntersect(mins, maxs, r_refdef.vieworg, r_refdef.vieworg)) if (R_CullBox(mins, maxs)) continue; @@ -168,74 +469,53 @@ void GL_DrawHeightmapModel (batch_t **batches, entity_t *e) b->ent = e; b->shader = hm->shader; b->flags = 0; - b->mesh = &hm->amesh[x+y*SECTIONS]; + b->mesh = &s->amesh; b->mesh[0] = mesh; b->meshes = 1; b->buildmeshes = NULL; - b->skin = &b->shader->defaulttextures; + b->skin = &s->textures; b->texture = NULL; - // vbo = b->vbo = hm->vbo[x+y*SECTIONS]; - b->vbo = NULL; + b->vbo = &s->vbo; + b->lightmap = s->lightmap; b->next = batches[b->shader->sort]; batches[b->shader->sort] = b; } } } +#endif unsigned int Heightmap_PointContentsHM(heightmap_t *hm, float clipmipsz, vec3_t org) { float x, y; float z, tz; int sx, sy; + int sectidx; + hmsection_t *s; - x = org[0]/hm->terrainscale; - y = org[1]/hm->terrainscale; - z = (org[2]+clipmipsz)/hm->heightscale; - - if (z < 0) + sx = org[0]/hm->sectionsize; + sy = org[1]/hm->sectionsize; + if (sx < 0 || sy < 0) return FTECONTENTS_SOLID; - if (z > 65535) + if (sx >= hm->numsegsx || sy >= hm->numsegsy) + return FTECONTENTS_SOLID; + sectidx = sx + sy*MAXSECTIONS; + s = hm->section[sectidx]; + if (!s) { - if (z > 65535+64 || clipmipsz) //top 64 units are sky + s = GL_LoadSection(hm, sx, sy); + if (!s) return FTECONTENTS_SOLID; - else - return FTECONTENTS_SKY; - } - if (x < 0) - { - if (x <= -1 || clipmipsz) - return FTECONTENTS_SOLID; - else - return FTECONTENTS_SKY; - } - if (y < 0) - { - if (x <= -1 || clipmipsz) - return FTECONTENTS_SOLID; - else - return FTECONTENTS_SKY; - } - if (x >= hm->terrainsize-1) - { - if (x >= hm->terrainsize || clipmipsz) - return FTECONTENTS_SOLID; - else - return FTECONTENTS_SKY; - } - if (y >= hm->terrainsize-1) - { - if (y >= hm->terrainsize || clipmipsz) - return FTECONTENTS_SOLID; - else - return FTECONTENTS_SKY; } + x = (org[0] - (sx*hm->sectionsize))*(SECTHEIGHTSIZE-1)/hm->sectionsize; + y = (org[1] - (sy*hm->sectionsize))*(SECTHEIGHTSIZE-1)/hm->sectionsize; + z = (org[2]+clipmipsz); + sx = x; x-=sx; sy = y; y-=sy; //made of two triangles: -#if 1 if (x+y>1) //the 1, 1 triangle { float v1, v2, v3; @@ -245,9 +525,9 @@ unsigned int Heightmap_PointContentsHM(heightmap_t *hm, float clipmipsz, vec3_t //0, 1 //1, 1 //1, 0 - tz = (hm->heights[(sx+0)+(sy+1)*hm->terrainsize]*v1 + - hm->heights[(sx+1)+(sy+1)*hm->terrainsize]*v2 + - hm->heights[(sx+1)+(sy+0)*hm->terrainsize]*v3); + tz = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE]*v1 + + s->heights[(sx+1)+(sy+1)*SECTHEIGHTSIZE]*v2 + + s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]*v3); } else { @@ -259,21 +539,10 @@ unsigned int Heightmap_PointContentsHM(heightmap_t *hm, float clipmipsz, vec3_t //0, 1 //1, 0 //0, 0 - tz = (hm->heights[(sx+0)+(sy+1)*hm->terrainsize]*v1 + - hm->heights[(sx+1)+(sy+0)*hm->terrainsize]*v2 + - hm->heights[(sx+0)+(sy+0)*hm->terrainsize]*v3); + tz = (s->heights[(sx+0)+(sy+1)*SECTHEIGHTSIZE]*v1 + + s->heights[(sx+1)+(sy+0)*SECTHEIGHTSIZE]*v2 + + s->heights[(sx+0)+(sy+0)*SECTHEIGHTSIZE]*v3); } -#else - { - float t, b; - //square? - //:( - - t = (hm->heights[sx+sy*hm->terrainsize]*(1-x) + hm->heights[sx+1+sy*hm->terrainsize]*x); - b = (hm->heights[sx+(sy+1)*hm->terrainsize]*(1-x) + hm->heights[sx+1+(sy+1)*hm->terrainsize]*x); - tz = t*(1-y) + b*y; - } -#endif if (z <= tz) return FTECONTENTS_SOLID; //contained within return FTECONTENTS_EMPTY; @@ -292,15 +561,28 @@ unsigned int Heightmap_NativeBoxContents(model_t *model, int hulloverride, int f void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) { + norm[0] = 0; + norm[1] = 0; + norm[2] = 1; +/* float x, y; float z; int sx, sy; vec3_t d1, d2; - x = org[0]/hm->terrainscale; - y = org[1]/hm->terrainscale; + x = org[0]/(SECTHEIGHTSIZE * hm->sectionsize); + y = org[1]/(SECTHEIGHTSIZE * hm->sectionsize); z = org[2]; + if (x < 0) + x = 0; + if (y < 0) + y = 0; + if (x > hm->tilesx-1) + x = hm->tilesx-1; + if (y > hm->tilesy-1) + y = hm->tilesy-1; + sx = x; x-=sx; sy = y; y-=sy; @@ -309,37 +591,40 @@ void Heightmap_Normal(heightmap_t *hm, vec3_t org, vec3_t norm) //0, 1 //1, 1 //1, 0 - d1[0] = hm->terrainscale; + d1[0] = (SECTHEIGHTSIZE * hm->sectionsize); d1[1] = 0; - d1[2] = hm->heights[(sx+1)+(sy+1)*hm->terrainsize] - hm->heights[(sx+0)+(sy+1)*hm->terrainsize]; + d1[2] = (hm->heights[(sx+1)+(sy+1)*hm->tilesx] - hm->heights[(sx+0)+(sy+1)*hm->tilesx]); d2[0] = 0; - d2[1] = hm->terrainscale; - d2[2] = hm->heights[(sx+1)+(sy+1)*hm->terrainsize] - hm->heights[(sx+1)+(sy+0)*hm->terrainsize]; + d2[1] = (SECTHEIGHTSIZE * hm->sectionsize); + d2[2] = (hm->heights[(sx+1)+(sy+1)*hm->tilesx] - hm->heights[(sx+1)+(sy+0)*hm->tilesx]); } else - { + { //the 0,0 triangle //0, 1 //1, 0 //0, 0 - d1[0] = hm->terrainscale; + d1[0] = (SECTHEIGHTSIZE * hm->sectionsize); d1[1] = 0; - d1[2] = hm->heights[(sx+0)+(sy+1)*hm->terrainsize] - hm->heights[(sx+0)+(sy+0)*hm->terrainsize]; + d1[2] = (hm->heights[(sx+0)+(sy+1)*hm->tilesx] - hm->heights[(sx+0)+(sy+0)*hm->tilesx]); d2[0] = 0; - d2[1] = hm->terrainscale; - d2[2] = hm->heights[(sx+1)+(sy+0)*hm->terrainsize] - hm->heights[(sx+0)+(sy+0)*hm->terrainsize]; + d2[1] = (SECTHEIGHTSIZE * hm->sectionsize); + d2[2] = (hm->heights[(sx+1)+(sy+0)*hm->tilesx] - hm->heights[(sx+0)+(sy+0)*hm->tilesx]); } + VectorNormalize(d1); + VectorNormalize(d2); CrossProduct(d1, d2, norm); VectorNormalize(norm); +*/ } #if 0 typedef struct { vec3_t start; vec3_t end; - vec3_t mins; - vec3_t maxs; vec3_t impact; + vec4_t plane; + float frac; heightmap_t *hm; int contents; } hmtrace_t; @@ -347,19 +632,154 @@ typedef struct { #define Closest(res,n,min,max) Closestf(res[0],n[0],min[0],max[0]);Closestf(res[1],n[1],min[1],max[1]);Closestf(res[2],n[2],min[2],max[2]) void Heightmap_Trace_Square(hmtrace_t *tr, int sx, int sy) { + vec3_t d[2]; + vec3_t p[3]; + vec4_t n[5]; + int t, i; + + qboolean startout, endout; + float *enterplane; + float enterfrac, exitfrac; + float enterdist=0; + float dist, d1, d2, f; + + if (sx < 0 || sx > tr->hm->tilesx) + return; + if (sy < 0 || sy > tr->hm->tilesy) + return; + + for (t = 0; t < 2; t++) + { + /*generate the brush*/ + if (t == 0) + { + VectorSet(p[0], tr->hm->terrainscale*(sx+0), tr->hm->terrainscale*(sy+0), tr->hm->heights[(sx+0)+(sy+0)*tr->hm->tilesx]); + VectorSet(p[1], tr->hm->terrainscale*(sx+1), tr->hm->terrainscale*(sy+0), tr->hm->heights[(sx+1)+(sy+0)*tr->hm->tilesx]); + VectorSet(p[2], tr->hm->terrainscale*(sx+0), tr->hm->terrainscale*(sy+1), tr->hm->heights[(sx+0)+(sy+1)*tr->hm->tilesx]); + VectorSubtract(p[1], p[0], d[0]); + VectorSubtract(p[2], p[0], d[1]); + //left-most + Vector4Set(n[0], -1, 0, 0, tr->hm->terrainscale*(sx+0)); + //top-most + Vector4Set(n[1], 0, -1, 0, tr->hm->terrainscale*(sy+0)); + //bottom-right + VectorSet(n[2], 0.70710678118654752440084436210485, 0.70710678118654752440084436210485, 0); + n[2][3] = -DotProduct(n[2], p[1]); + //top + CrossProduct(d[0], d[1], n[3]); + VectorNormalize(n[3]); + n[3][3] = -DotProduct(n[3], p[1]); + //down + Vector4Set(n[4], 0, 0, 1, 0); + } + else + { + VectorSet(p[0], tr->hm->terrainscale*(sx+1), tr->hm->terrainscale*(sy+1), tr->hm->heights[(sx+1)+(sy+1)*tr->hm->tilesx]); + VectorSet(p[1], tr->hm->terrainscale*(sx+1), tr->hm->terrainscale*(sy+0), tr->hm->heights[(sx+1)+(sy+0)*tr->hm->tilesx]); + VectorSet(p[2], tr->hm->terrainscale*(sx+0), tr->hm->terrainscale*(sy+1), tr->hm->heights[(sx+0)+(sy+1)*tr->hm->tilesx]); + VectorSubtract(p[1], p[0], d[0]); + VectorSubtract(p[2], p[0], d[1]); + //right-most + Vector4Set(n[0], 1, 0, 0, tr->hm->terrainscale*(sx+1)); + //bottom-most + Vector4Set(n[1], 0, 1, 0, tr->hm->terrainscale*(sy+1)); + //bottom-right + VectorSet(n[2], -0.70710678118654752440084436210485, -0.70710678118654752440084436210485, 0); + n[2][3] = -DotProduct(n[2], p[1]); + //top + CrossProduct(d[0], d[1], n[3]); + VectorNormalize(n[3]); + n[3][3] = -DotProduct(n[3], p[1]); + //down + Vector4Set(n[4], 0, 0, 1, 0); + } + + + + + startout = false; + endout = false; + enterplane= NULL; + enterfrac = -1; + exitfrac = 10; + for (i = 0; i < 5; i++) + { + /*calculate the distance based upon the shape of the object we're tracing for*/ + dist = n[i][3]; + + + d1 = DotProduct (tr->start, n[i]) - dist; + d2 = DotProduct (tr->end, n[i]) - dist; + + if (d1 >= 0) + startout = true; + if (d2 > 0) + endout = true; + + //if we're fully outside any plane, then we cannot possibly enter the brush, skip to the next one + if (d1 > 0 && d2 >= 0) + goto nextbrush; + + //if we're fully inside the plane, then whatever is happening is not relevent for this plane + if (d1 < 0 && d2 <= 0) + continue; + + f = d1 / (d1-d2); + if (d1 > d2) + { + //entered the brush. favour the furthest fraction to avoid extended edges (yay for convex shapes) + if (enterfrac < f) + { + enterfrac = f; + enterplane = n[i]; + enterdist = dist; + } + } + else + { + //left the brush, favour the nearest plane (smallest frac) + if (exitfrac > f) + { + exitfrac = f; + } + } + } + + if (!startout) + { + tr->frac = -1; + return; + } + if (enterfrac != -1 && enterfrac < exitfrac) + { + //impact! + if (enterfrac < tr->frac) + { + tr->frac = enterfrac; + tr->plane[3] = enterdist; + VectorCopy(enterplane, tr->plane); + } + } +nextbrush: + ; + } +#if 0 float normf = 0.70710678118654752440084436210485; float pd, sd, ed, bd; int tris, x, y; vec3_t closest; vec3_t point; + pd = normf*(x+y); sd = normf*tr->start[0]+normf*tr->start[1]; ed = normf*tr->end[0]+normf*tr->end[1]; bd = normf*tr->maxs[0]+normf*tr->maxs[1]; //assume mins is this but negative //see which of the two triangles in the square it travels over. - tris = sd<=pd || ed<=pd; + tris = 0; + if (sd<=pd || ed<=pd) + tris |= 1; if (sd>=pd || ed>=pd) tris |= 2; @@ -375,8 +795,8 @@ void Heightmap_Trace_Square(hmtrace_t *tr, int sx, int sy) x = tr->hm->heights[(sx+1)+(sy+0)*tr->hm->terrainsize] - tr->hm->heights[(sx+0)+(sy+0)*tr->hm->terrainsize]; y = tr->hm->heights[(sx+0)+(sy+1)*tr->hm->terrainsize] - tr->hm->heights[(sx+0)+(sy+0)*tr->hm->terrainsize]; - norm[0] = (-x*tr->hm->heightscale)/tr->hm->terrainscale; - norm[1] = (-y*tr->hm->heightscale)/tr->hm->terrainscale; + norm[0] = (-x)/tr->hm->terrainscale; + norm[1] = (-y)/tr->hm->terrainscale; norm[2] = 1.0f/(float)sqrt(norm[0]*norm[0] + norm[1]*norm[1] + 1); Closest(closest, norm, tr->mins, tr->maxs); dc = DotProduct(norm, closest) - DotProduct(norm, point); @@ -399,8 +819,8 @@ void Heightmap_Trace_Square(hmtrace_t *tr, int sx, int sy) { //triangle with 1, 1 vec3_t norm; float d1, d2, dc; - norm[0] = (-x*tr->hm->heightscale)/tr->hm->terrainscale; - norm[1] = (-y*tr->hm->heightscale)/tr->hm->terrainscale; + norm[0] = (-x)/tr->hm->terrainscale; + norm[1] = (-y)/tr->hm->terrainscale; norm[2] = 1.0f/(float)sqrt(norm[0]*norm[0] + norm[1]*norm[1] + 1); Closest(closest, norm, tr->mins, tr->maxs); dc = DotProduct(norm, closest) - DotProduct(norm, point); @@ -419,74 +839,41 @@ void Heightmap_Trace_Square(hmtrace_t *tr, int sx, int sy) tr->impact[2] = tr->end[2]*d1+tr->start[2]*d2; } } +#endif } -void Heightmap_Trace_Y(hmtrace_t *tr, int x, int min, int max) +#define DIST_EPSILON 0 +void Heightmap_RecurseTrace(hmtrace_t *tr, float p1[2], float p2[2]) { + float newv[2]; + float frac; int mid; - if (min == max) + int axis; + //FIXME: expand the trace somehow + if ((int)p1[0] == (int)p2[0] && (int)p1[1] == (int)p2[1]) { //end - Heightmap_Trace_Square(tr, x, min); - return; - } - mid = ((max-min)>>1)+min; - if (tr->start[1] < min+tr->mins[1] && tr->end[1] < min+tr->mins[1]) - { //both on one size. - Heightmap_Trace_Y(tr, x, min, mid); - return; - } - if (tr->start[1] > max+tr->maxs[1] && tr->end[1] > max+tr->maxs[1]) - { //both on one size. - Heightmap_Trace_Y(tr, x, mid, max); + Heightmap_Trace_Square(tr, p1[0], p2[1]); return; } + /*decide the plane axis*/ + axis = abs(p2[1] - p1[1]) > abs(p2[0] - p1[0]); + /*figure out the index to split the trace at*/ + mid = (p1[axis] + p2[axis])*0.5; + if (!mid)/*make sure we make progress*/ + mid = (p2[axis] > p1[axis])?1:-1; - //crosses this line. - if (tr->start[1] > tr->end[1]) - { - Heightmap_Trace_Y(tr, x, min, mid); - if (!tr->contents) - Heightmap_Trace_Y(tr, x, mid, max); - } + //it crosses somewhere, it must do. + if (p2[axis] > p1[axis]) + frac = ((p1[axis] - mid) - DIST_EPSILON)/(p1[axis]-p2[axis]); else - { - Heightmap_Trace_Y(tr, x, mid, max); - if (!tr->contents) - Heightmap_Trace_Y(tr, x, min, mid); - } -} -void Heightmap_Trace_X(hmtrace_t *tr, int min, int max) -{ - int mid; - if (min == max) - { //end - //FIXME: we don't have to check ALL squares like this, we could use a much smaller range. - Heightmap_Trace_Y(tr, min, 0, tr->hm->terrainsize); - return; - } - mid = ((max-min)>>1)+min; - if (tr->start[0] < min+tr->mins[0] && tr->end[0] < min+tr->mins[0]) - { //both on one size. - Heightmap_Trace_X(tr, min, mid); - return; - } - if (tr->start[0] > max+tr->maxs[0] && tr->end[0] > max+tr->maxs[0]) - { //both on one size. - Heightmap_Trace_X(tr, mid, max); - return; - } + frac = ((p1[axis] - mid) + DIST_EPSILON)/(p1[axis]-p2[axis]); - //crosses this line. - if (tr->start[0] > tr->end[0]) + newv[axis] = mid; + newv[!axis] = (p2[!axis] * frac) + (p1[!axis] * (1-frac)); + if ((int)p1[axis] != (int)newv[axis]) + Heightmap_RecurseTrace(tr, p1, newv); + if (!tr->contents) { - Heightmap_Trace_X(tr, min, mid); - if (!tr->contents) - Heightmap_Trace_X(tr, mid, max); - } - else - { - Heightmap_Trace_X(tr, mid, max); - if (!tr->contents) - Heightmap_Trace_X(tr, min, mid); + Heightmap_RecurseTrace(tr, newv, p2); } } @@ -501,26 +888,43 @@ Why is recursion good? Obviously, we don't care all that much about 1 */ -qboolean Heightmap_Trace(model_t *model, int forcehullnum, int frame, vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, trace_t *trace) +qboolean Heightmap_Trace(struct model_s *model, int hulloverride, int frame, vec3_t axis[3], vec3_t start, vec3_t end, vec3_t mins, vec3_t maxs, unsigned int against, struct trace_s *trace) { + float p1[2], p2[2]; hmtrace_t hmtrace; hmtrace.hm = model->terrain; hmtrace.start[0] = start[0]/hmtrace.hm->terrainscale; hmtrace.start[1] = start[1]/hmtrace.hm->terrainscale; - hmtrace.start[2] = (start[2])/hmtrace.hm->heightscale; + hmtrace.start[2] = (start[2] + mins[2]); hmtrace.end[0] = end[0]/hmtrace.hm->terrainscale; hmtrace.end[1] = end[1]/hmtrace.hm->terrainscale; - hmtrace.end[2] = (end[2])/hmtrace.hm->heightscale; - hmtrace.mins[0] = mins[0]/hmtrace.hm->terrainscale; - hmtrace.mins[1] = mins[1]/hmtrace.hm->terrainscale; - hmtrace.mins[2] = (mins[2])/hmtrace.hm->heightscale; - hmtrace.maxs[0] = maxs[0]/hmtrace.hm->terrainscale; - hmtrace.maxs[1] = maxs[1]/hmtrace.hm->terrainscale; - hmtrace.maxs[2] = (maxs[2])/hmtrace.hm->heightscale; + hmtrace.end[2] = (end[2] + mins[2]); - //FIXME: we don't have to check ALL squares like this, we could use a much smaller range. - Heightmap_Trace_X(&hmtrace, 0, hmtrace.hm->terrainsize); + p1[0] = (start[0])/hmtrace.hm->terrainscale; + p1[1] = (start[1])/hmtrace.hm->terrainscale; + p2[0] = (end[0])/hmtrace.hm->terrainscale; + p2[1] = (end[1])/hmtrace.hm->terrainscale; + + Heightmap_RecurseTrace(&hmtrace, p1, p2); + + trace->plane.dist = hmtrace.plane[3]; + trace->plane.normal[0] = hmtrace.plane[0]; + trace->plane.normal[1] = hmtrace.plane[1]; + trace->plane.normal[2] = hmtrace.plane[2]; + + if (hmtrace.frac == -1) + { + trace->fraction = 0; + trace->startsolid = true; + trace->allsolid = true; + } + else + { + trace->fraction = hmtrace.frac; + VectorInterpolate(start, hmtrace.frac, end, trace->endpos); + } + return trace->fraction < 1; } #else /* @@ -609,6 +1013,18 @@ void Heightmap_FindTouchedLeafs (model_t *mod, pvscache_t *ent, float *mins, flo void Heightmap_LightPointValues (model_t *mod, vec3_t point, vec3_t res_diffuse, vec3_t res_ambient, vec3_t res_dir) { + float time = realtime; + + res_diffuse[0] = 128; + res_diffuse[1] = 128; + res_diffuse[2] = 128; + res_ambient[0] = 64; + res_ambient[1] = 64; + res_ambient[2] = 64; + res_dir[0] = sin(time); + res_dir[1] = cos(time); + res_dir[2] = sin(time); + VectorNormalize(res_dir); } void Heightmap_StainNode (mnode_t *node, float *parms) { @@ -627,159 +1043,315 @@ int Heightmap_LeafForPoint (model_t *model, vec3_t point) return 0; } -//Heightmap_NativeBoxContents +#ifndef SERVERONLY +static unsigned char *ted_getlightmap(hmsection_t *s, int idx) +{ + unsigned char *lm; + int x = idx % SECTTEXSIZE, y = idx / SECTTEXSIZE; + if (s->lightmap < 0) + { + s->lightmap = Surf_LM_AllocBlock(SECTTEXSIZE, SECTTEXSIZE, &s->lmx, &s->lmy, NULL); + BE_UploadAllLightmaps(); + } + lightmap[s->lightmap]->modified = true; + lightmap[s->lightmap]->rectchange.l = 0; + lightmap[s->lightmap]->rectchange.t = 0; + lightmap[s->lightmap]->rectchange.w = LMBLOCK_WIDTH; + lightmap[s->lightmap]->rectchange.h = LMBLOCK_HEIGHT; + lm = lightmap[s->lightmap]->lightmaps; + lm += ((s->lmx+y) * LMBLOCK_WIDTH + (s->lmy+x)) * lightmap_bytes; + return lm; +} +static void ted_heighttally(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w) +{ + /*raise the terrain*/ + ((float*)ctx)[0] += s->heights[idx]*w; + ((float*)ctx)[1] += w; +} +static void ted_heightsmooth(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w) +{ + s->modified = true; + /*interpolate the terrain towards a certain value*/ + s->heights[idx] = s->heights[idx]*(1-w) + w**(float*)ctx; +} +static void ted_heightraise(void *ctx, hmsection_t *s, int idx, float wx, float wy, float strength) +{ + s->modified = true; + /*raise the terrain*/ + s->heights[idx] += strength; +} +static void ted_heightset(void *ctx, hmsection_t *s, int idx, float wx, float wy, float strength) +{ + s->modified = true; + /*set the terrain to a specific value*/ + s->heights[idx] = *(float*)ctx; +} + +static void ted_mixconcentrate(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w) +{ + unsigned char *lm = ted_getlightmap(s, idx); + /*concentrate the lightmap values to a single channel*/ + if (lm[0] > lm[1] && lm[0] > lm[2] && lm[0] > (255-(lm[0]+lm[1]+lm[2]))) + { + lm[0] = lm[0]*(1-w) + 255*(w); + lm[1] = lm[1]*(1-w) + 0*(w); + lm[2] = lm[2]*(1-w) + 0*(w); + } + else if (lm[1] > lm[2] && lm[1] > (255-(lm[0]+lm[1]+lm[2]))) + { + lm[0] = lm[0]*(1-w) + 0*(w); + lm[1] = lm[1]*(1-w) + 255*(w); + lm[2] = lm[2]*(1-w) + 0*(w); + } + else if (lm[2] > (255-(lm[0]+lm[1]+lm[2]))) + { + lm[0] = lm[0]*(1-w) + 0*(w); + lm[1] = lm[1]*(1-w) + 0*(w); + lm[2] = lm[2]*(1-w) + 255*(w); + } + else + { + lm[0] = lm[0]*(1-w) + 0*(w); + lm[1] = lm[1]*(1-w) + 0*(w); + lm[2] = lm[2]*(1-w) + 0*(w); + } +} + +static void ted_mixnoise(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w) +{ + unsigned char *lm = ted_getlightmap(s, idx); + vec4_t v; + float sc; + /*randomize the lightmap somewhat (you'll probably want to concentrate it a bit after)*/ + v[0] = (rand()&255); + v[1] = (rand()&255); + v[2] = (rand()&255); + v[3] = (rand()&255); + sc = v[0] + v[1] + v[2] + v[3]; + Vector4Scale(v, 255/sc, v); + + lm[0] = lm[0]*(1-w) + (v[0]*(w)); + lm[1] = lm[1]*(1-w) + (v[1]*(w)); + lm[2] = lm[2]*(1-w) + (v[2]*(w)); +} + +static void ted_mixset(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w) +{ + unsigned char *lm = ted_getlightmap(s, idx); + lm[2] = lm[2]*(1-w) + (255*((float*)ctx)[0]*(w)); + lm[1] = lm[1]*(1-w) + (255*((float*)ctx)[1]*(w)); + lm[0] = lm[0]*(1-w) + (255*((float*)ctx)[2]*(w)); +} + +static void ted_mixtally(void *ctx, hmsection_t *s, int idx, float wx, float wy, float w) +{ + unsigned char *lm = ted_getlightmap(s, idx); + ((float*)ctx)[0] += lm[0]*w; + ((float*)ctx)[1] += lm[1]*w; + ((float*)ctx)[2] += lm[2]*w; + ((float*)ctx)[3] += w; +} + + +//calls 'func' for each tile upon the terrain. the 'tile' can be either height or texel +static void ted_itterate(heightmap_t *hm, float *pos, float radius, float strength, int steps, void(*func)(void *ctx, hmsection_t *s, int idx, float wx, float wy, float strength), void *ctx) +{ + int tx, ty; + float wx, wy; + float sc[2]; + int min[2], max[2]; + int sx,sy; + hmsection_t *s; + float w, xd, yd; + + min[0] = floor((pos[0] - radius)/(hm->sectionsize) - 1); + min[1] = floor((pos[1] - radius)/(hm->sectionsize) - 1); + max[0] = ceil((pos[0] + radius)/(hm->sectionsize) + 1); + max[1] = ceil((pos[1] + radius)/(hm->sectionsize) + 1); + + min[0] = bound(0, min[0], hm->numsegsx); + min[1] = bound(0, min[1], hm->numsegsy); + max[0] = bound(0, max[0], hm->numsegsx); + max[1] = bound(0, max[1], hm->numsegsy); + + sc[0] = hm->sectionsize/(steps-1); + sc[1] = hm->sectionsize/(steps-1); + + for (sx = min[0]; sx < max[0]; sx++) + { + for (sy = min[1]; sy < max[1]; sy++) + { + s = hm->section[(int)(sx) + (int)(sy)*MAXSECTIONS]; + if (!s) + s = GL_LoadSection(hm, sx, sy); + if (!s) + continue; + + for (tx = 0; tx < steps; tx++) + { + for (ty = 0; ty < steps; ty++) + { + /*both heights and textures have an overlapping/matching sample at the edge, there's no need for any half-pixels or anything here*/ + wx = (sx*(steps-1.0) + tx)*sc[0]; + wy = (sy*(steps-1.0) + ty)*sc[1]; + xd = wx - pos[0]; + yd = wy - pos[1]; + w = sqrt(radius*radius - (xd*xd+yd*yd)); + if (w > 0) + { + func(ctx, s, tx+ty*steps, wx, wy, w*strength/(radius)); + } + } + } + } + } +} + +//Heightmap_NativeBoxContents enum { ter_reload, ter_save, - ter_set, - ter_smooth, + ter_height_set, + ter_height_smooth, ter_raise, - ter_lower + ter_lower, + ter_tex_set, + ter_tex_get, + ter_mixset, + ter_mixconcentrate, + ter_mixnoise, + ter_mixblur, }; -qboolean Heightmap_Edit(model_t *mod, int action, float *pos, float radius, float quant) +void QCBUILTIN PF_terrain_edit(progfuncs_t *prinst, struct globalvars_s *pr_globals) { - unsigned short *tmp; - heightmap_t *hm = mod->terrain; - int size; - int x, y, min[2], max[2]; - float xd, yd, w; - vec2_t sc; - if (mod->type != mod_heightmap) - return false; + world_t *vmw = prinst->parms->user; + int action = G_FLOAT(OFS_PARM0); + float *pos = G_VECTOR(OFS_PARM1); + float radius = G_FLOAT(OFS_PARM2); + float quant = G_FLOAT(OFS_PARM3); +// G_FLOAT(OFS_RETURN) = Heightmap_Edit(w->worldmodel, action, pos, radius, quant); - size = hm->terrainsize; + model_t *mod = vmw->worldmodel; + heightmap_t *hm; + vec4_t tally; - if (radius < 0.05) - radius = 0.05; + G_FLOAT(OFS_RETURN) = 0; - sc[0] = pos[0] / hm->terrainscale; - sc[1] = pos[1] / hm->terrainscale; - radius = radius / hm->terrainscale; - quant /= hm->heightscale; - - min[0] = sc[0] - radius; - min[1] = sc[1] - radius; - max[0] = sc[0] + radius; - max[1] = sc[1] + radius; - - if (min[0] < 0) - min[0] = 0; - if (min[1] < 0) - min[1] = 0; - - if (max[0] > size) - max[0] = size; - if (max[1] < size) - max[1] = size; + if (!mod || mod->type != mod_heightmap) + return; + hm = mod->terrain; switch(action) { case ter_reload: - tmp = (unsigned short*)COM_LoadTempFile(hm->hmapname); - if (tmp) - x = com_filesize/2; - else - x = 0; - if (x > size*size) - x = size*size; - while (x-- > 0) - { - hm->heights[x] = LittleShort(tmp[x]); - } + HeightMap_Purge(mod); break; case ter_save: - tmp = Hunk_TempAlloc(size*size*2); - for (x = 0; x < size*size; x++) - { - tmp[x] = LittleShort(hm->heights[x]); - } - COM_WriteFile(hm->hmapname, tmp, size*size*2); + HeightMap_Save(hm); break; - case ter_set: - for (x = min[0]; x < max[0]; x++) - { - for (y = min[1]; y < max[1]; y++) - { - xd = sc[0] - x; - yd = sc[1] - y; - if (sqrt(xd*xd+yd*yd) < radius) - { - hm->heights[x + y*size] = quant; - hm->modified[(int)(x/(hm->terrainscale)) + (int)(y/(hm->terrainscale))*SECTIONS] = true; - } - } - } + case ter_height_set: + ted_itterate(hm, pos, radius, 1, SECTHEIGHTSIZE, ted_heightset, &quant); break; - case ter_smooth: - case ter_raise: - for (x = min[0]; x < max[0]; x++) - { - for (y = min[1]; y < max[1]; y++) - { - xd = sc[0] - x; - yd = sc[1] - y; - w = sqrt(radius*radius - (xd*xd+yd*yd)); - if (w > 0) - { - w *= quant/radius; - hm->heights[x + y*size] += w; - hm->modified[(int)(x/(hm->terrainscale)) + (int)(y/(hm->terrainscale))*SECTIONS] = true; - } - } - } + case ter_height_smooth: + tally[0] = 0; + tally[1] = 0; + ted_itterate(hm, pos, radius, 1, SECTHEIGHTSIZE, ted_heighttally, &tally); + tally[0] /= tally[1]; + ted_itterate(hm, pos, radius, 1, SECTHEIGHTSIZE, ted_heightsmooth, &tally); break; case ter_lower: - for (x = min[0]; x < max[0]; x++) + quant *= -1; + case ter_raise: + ted_itterate(hm, pos, radius, quant, SECTHEIGHTSIZE, ted_heightraise, &quant); + break; + case ter_mixset: + ted_itterate(hm, pos, radius, 1, SECTTEXSIZE, ted_mixset, G_VECTOR(OFS_PARM4)); + break; + case ter_mixconcentrate: + ted_itterate(hm, pos, radius, 1, SECTTEXSIZE, ted_mixconcentrate, NULL); + break; + case ter_mixnoise: + ted_itterate(hm, pos, radius, 1, SECTTEXSIZE, ted_mixnoise, NULL); + break; + case ter_mixblur: + Vector4Set(tally, 0, 0, 0, 0); + ted_itterate(hm, pos, radius, 1, SECTTEXSIZE, ted_mixtally, &tally); + VectorScale(tally, 1/tally[3], tally); + ted_itterate(hm, pos, radius, quant, SECTTEXSIZE, ted_mixset, &tally); + break; + case ter_tex_set: + ted_itterate(hm, pos, radius, 1, SECTTEXSIZE, ted_mixset, NULL); +/* radius *= (float)hm->numsegsx / hm->tilesx; + for (x = 0; x < hm->numsegsx; x++) { - for (y = min[1]; y < max[1]; y++) + for (y = 0; y < hm->numsegsy; y++) { - xd = sc[0] - x; - yd = sc[1] - y; + xd = (sc[0] - x) * (float)hm->numsegsx / hm->tilesx; + yd = (sc[1] - y) * (float)hm->numsegsy / hm->tilesy; w = sqrt(radius*radius - (xd*xd+yd*yd)); if (w > 0) { - w *= quant/radius; - /*don't drop below 0*/ - if (hm->heights[x + y*size]-w < 0) - hm->heights[x + y*size] = 0; - else - hm->heights[x + y*size] -= w; - hm->modified[(int)(x/(hm->terrainscale)) + (int)(y/(hm->terrainscale))*SECTIONS] = true; + s = hm->section[(int)(x) + (int)(y)*MAXSECTIONS]; + if (!s) + s = GL_LoadSection(hm, x, y); + if (s) + { + if (quant < 0 || quant >= 4) + quant = 0; + Q_strncpyz(s->texname[(int)quant], PR_GetStringOfs(prinst, OFS_PARM4), sizeof(s->texname[0])); + s->modified = true; + + GL_LoadSectionTextures(s); + } } } } +*/ + break; + case ter_tex_get: +/* + x = sc[0]*hm->numsegsx / hm->tilesx; + y = sc[1]*hm->numsegsy / hm->tilesy; + x = bound(0, x, hm->numsegsx-1); + y = bound(0, y, hm->numsegsy-1); + + G_INT(OFS_RETURN) = 0; + s = hm->section[(int)(x) + (int)(y)*MAXSECTIONS]; + if (!s) + s = GL_LoadSection(hm, x, y); + if (s) + { + x = bound(0, quant, 3); + G_INT(OFS_RETURN) = PR_TempString(prinst, s->texname[x]); + } +*/ break; } - - return true; } +#else +void QCBUILTIN PF_terrain_edit(progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + G_FLOAT(OFS_RETURN) = 0; +} +#endif qboolean GL_LoadHeightmapModel (model_t *mod, void *buffer) { heightmap_t *hm; - unsigned short *heightmap; - int size; - int x, y; float skyrotate; vec3_t skyaxis; - char heightmapname[MAX_QPATH]; - char detailtexname[MAX_QPATH]; - char basetexname[MAX_QPATH]; - char exttexname[MAX_QPATH]; + char shadername[MAX_QPATH]; char entfile[MAX_QPATH]; char skyname[MAX_QPATH]; - float worldsize = 64; - float heightsize = 1/16; - int numsegs = 1; + int numsegsx = 0, numsegsy = 0; + int sectsize = 0; - *heightmapname = '\0'; - *detailtexname = '\0'; - *basetexname = '\0'; - *exttexname = '\0'; - *entfile = '\0'; + COM_FileBase(mod->name, shadername, sizeof(shadername)); + Q_snprintfz(entfile, sizeof(entfile), "maps/%s/entities.ent", shadername); + strcpy(shadername, "terrainshader"); strcpy(skyname, "night"); skyrotate = 0; @@ -794,44 +1366,21 @@ qboolean GL_LoadHeightmapModel (model_t *mod, void *buffer) return false; } - if (qrenderer != QR_OPENGL) - return false; - for(;;) { buffer = COM_Parse(buffer); if (!buffer) break; - if (!strcmp(com_token, "heightmap")) + if (!strcmp(com_token, "shadername")) { buffer = COM_Parse(buffer); - Q_strncpyz(heightmapname, com_token, sizeof(heightmapname)); + Q_strncpyz(shadername, com_token, sizeof(shadername)); } - else if (!strcmp(com_token, "detail")) + else if (!strcmp(com_token, "segmentsize")) //size of each segment in quake units { buffer = COM_Parse(buffer); - Q_strncpyz(detailtexname, com_token, sizeof(detailtexname)); - } - else if (!strcmp(com_token, "texturegridbase")) - { - buffer = COM_Parse(buffer); - Q_strncpyz(basetexname, com_token, sizeof(basetexname)); - } - else if (!strcmp(com_token, "texturegridext")) - { - buffer = COM_Parse(buffer); - Q_strncpyz(exttexname, com_token, sizeof(exttexname)); - } - else if (!strcmp(com_token, "gridsize")) - { - buffer = COM_Parse(buffer); - worldsize = atof(com_token); - } - else if (!strcmp(com_token, "heightsize")) - { - buffer = COM_Parse(buffer); - heightsize = atof(com_token); + sectsize = atof(com_token); } else if (!strcmp(com_token, "entfile")) { @@ -856,87 +1405,86 @@ qboolean GL_LoadHeightmapModel (model_t *mod, void *buffer) else if (!strcmp(com_token, "texturesegments")) { buffer = COM_Parse(buffer); - numsegs = atoi(com_token); + numsegsx = numsegsy = atoi(com_token); + } + else if (!strcmp(com_token, "texturesegmentsx")) + { + buffer = COM_Parse(buffer); + numsegsx = atoi(com_token); + } + else if (!strcmp(com_token, "texturesegmentsy")) + { + buffer = COM_Parse(buffer); + numsegsy = atoi(com_token); } else { - Con_Printf(CON_ERROR "%s, unrecognised token in terrain map\n", mod->name); + Con_Printf(CON_ERROR "%s, unrecognised token \"%s\" in terrain map\n", mod->name, com_token); return false; } } - if (numsegs > SECTIONS) + if (!sectsize) + sectsize = 1024; + + if (!numsegsx) + numsegsx = 16; + if (!numsegsy) + numsegsy = 16; + + if (numsegsx > MAXSECTIONS || numsegsy > MAXSECTIONS) { - Con_Printf(CON_ERROR "%s, heightmap uses too many sections max is %i\n", mod->name, SECTIONS); + Con_Printf(CON_ERROR "%s, heightmap uses too many sections max is %i\n", mod->name, MAXSECTIONS); + return false; + } + mod->type = mod_heightmap; + + hm = BZ_Malloc(sizeof(*hm)); + memset(hm, 0, sizeof(*hm)); + COM_FileBase(mod->name, hm->path, sizeof(hm->path)); + + mod->entities = COM_LoadHunkFile(entfile); + if (!mod->entities) + { + BZ_Free(hm); + Con_Printf(CON_ERROR "unable to read %s\n", entfile); return false; } + hm->sectionsize = sectsize; + hm->numsegsx = numsegsx; + hm->numsegsy = numsegsy; - mod->type = mod_heightmap; - - heightmap = (unsigned short*)COM_LoadTempFile(heightmapname); - if (!heightmap) - { - size = 1024; - heightmap = Hunk_TempAlloc(size*size*2); - - for (x = 0; x < size; x++) - for (y = 0; y < size; y++) - { - heightmap[x+y*size] = 0; - } - } - else - { - size = sqrt(com_filesize/2); - if (size % numsegs) - { - Con_Printf(CON_ERROR "%s, heightmap is not a multiple of %i\n", mod->name, numsegs); - return false; - } - } - - hm = Hunk_Alloc(sizeof(*hm) + size*size*2); - memset(hm, 0, sizeof(*hm)); - Q_strncpyz(hm->hmapname, heightmapname, sizeof(hm->hmapname)); - hm->heights = (unsigned short*)(hm+1); - for (x = 0; x < size*size; x++) - { - hm->heights[x] = LittleShort(heightmap[x]); - } - hm->terrainsize = size; - hm->terrainscale = worldsize; - hm->heightscale = heightsize; - hm->numsegs = numsegs; - - hm->shader = R_RegisterShader(basetexname, - "{\n" - "{\n" - "map maps/test/ground.jpg\n" - "}\n" - "}\n" - ); - - hm->skyshader = R_RegisterCustom(va("skybox_%s", skyname), Shader_DefaultSkybox, NULL); - - - mod->entities = COM_LoadHunkFile(entfile); - +#ifndef SERVERONLY if (qrenderer != QR_NONE) { - hm->detailtexture = R_LoadHiResTexture(detailtexname, "", IF_NOGAMMA); - - for (x = 0; x < numsegs; x++) - { - int y; - - for (y = 0; y < numsegs; y++) - { - hm->textures[x+y*SECTIONS] = R_LoadHiResTexture(va("%s%02ix%02i%s", basetexname, x, y, exttexname), "", IF_CLAMP|IF_NOGAMMA); - hm->modified[x+y*SECTIONS] = true; - } - } + hm->shader = R_RegisterShader(shadername, + "{\n" + "{\n" + "map $diffuse\n" + "}\n" + "{\n" + "map $upperoverlay\n" + "}\n" + "{\n" + "map $loweroverlay\n" + "}\n" + "{\n" + "map $fullbright\n" + "}\n" + "{\n" + "map $lightmap\n" + "}\n" + "program terrain\n" + "if r_terraindebug\n" + "[\n" + "program terraindebug\n" + "]\n" + "}\n" + ); + hm->skyshader = R_RegisterCustom(va("skybox_%s", skyname), Shader_DefaultSkybox, NULL); } +#endif mod->funcs.NativeTrace = Heightmap_Trace; mod->funcs.PointContents = Heightmap_PointContents; diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index b863d75ae..71277dc2b 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -359,6 +359,12 @@ void RMod_ClearAll (void) BE_ClearVBO(&mod->textures[t]->vbo); } } +#ifdef TERRAIN + if (mod->type == mod_heightmap) + { + HeightMap_Purge(mod); + } +#endif if (mod->type != mod_alias && mod->type != mod_halflife diff --git a/engine/gl/gl_ngraph.c b/engine/gl/gl_ngraph.c index 9158119f7..ab048fa59 100644 --- a/engine/gl/gl_ngraph.c +++ b/engine/gl/gl_ngraph.c @@ -172,6 +172,7 @@ void R_NetgraphInit(void) TEXASSIGN(netgraphtexture, R_AllocNewTexture("***netgraph***", NET_TIMINGS, NET_GRAPHHEIGHT)); netgraphshader = R_RegisterShader("netgraph", "{\n" + "program default2d\n" "{\n" "map $diffuse\n" "blendfunc blend\n" diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index b1124f4ac..14f351348 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -850,7 +850,7 @@ static void R_RenderMotionBlur(void) #ifdef warningmsg #pragma warningmsg("backend fixme") #endif -#ifndef ANDROID +#if !defined(ANDROID) && !defined(NACL) if (gl_config.arb_texture_non_power_of_two) { //we can use any size, supposedly vwidth = vid.pixelwidth; diff --git a/engine/gl/gl_rmisc.c b/engine/gl/gl_rmisc.c index d18eb9db6..e8ae0d62d 100644 --- a/engine/gl/gl_rmisc.c +++ b/engine/gl/gl_rmisc.c @@ -589,7 +589,8 @@ void GLR_TimeRefresh_f (void) else #endif { - qglDrawBuffer (GL_FRONT); + if (qglDrawBuffer) + qglDrawBuffer (GL_FRONT); qglFinish (); start = Sys_DoubleTime (); @@ -606,7 +607,8 @@ void GLR_TimeRefresh_f (void) time = stop-start; Con_Printf ("%f seconds (%f fps)\n", time, frames/time); - qglDrawBuffer (GL_BACK); + if (qglDrawBuffer) + qglDrawBuffer (GL_BACK); GL_EndRendering (); GL_DoSwap(); } diff --git a/engine/gl/gl_screen.c b/engine/gl/gl_screen.c index e6ccd2430..237138f2d 100644 --- a/engine/gl/gl_screen.c +++ b/engine/gl/gl_screen.c @@ -168,6 +168,7 @@ void GLSCR_UpdateScreen (void) V_RenderView (); else { + GL_DoSwap(); noworld = true; } } diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 4e574d54a..32dff6db6 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -299,7 +299,13 @@ static qboolean Shader_EvaluateCondition(char **ptr) else { cv = Cvar_Get(token, "", 0, "Shader Conditions"); + if (!cv) + { + Con_Printf("Shader_EvaluateCondition: '%s' is not a cvar\n", token); + return conditiontrue; + } token = COM_ParseExt ( ptr, false ); + cv->flags |= CVAR_SHADERSYSTEM; if (*token) { float rhs; @@ -1078,8 +1084,7 @@ struct sbuiltin_s "void main ()\n" "{\n" - " gl_FragColor = fog4blend(texture2D(s_t0, tc) * vc);\n" - " gl_FragColor = gl_FragColor * e_colourident;\n" + " gl_FragColor = fog4blend(texture2D(s_t0, tc) * vc * e_colourident);\n" "}\n" "#endif\n" }, @@ -1108,8 +1113,7 @@ struct sbuiltin_s "void main ()\n" "{\n" - " gl_FragColor = fog4additive(texture2D(s_t0, tc) * vc);\n" - " gl_FragColor = gl_FragColor * e_colourident;\n" + " gl_FragColor = fog4additive(texture2D(s_t0, tc) * vc * e_colourident);\n" "}\n" "#endif\n" }, @@ -1206,10 +1210,42 @@ struct sbuiltin_s "#ifdef FULLBRIGHT\n" " gl_FragColor.rgb += texture2D(s_t4, tc).rgb;\n" "#endif\n" + "gl_FragColor = gl_FragColor * e_colourident;\n" "#ifdef FOG\n" "gl_FragColor = fog4(gl_FragColor);\n" "#endif\n" - "gl_FragColor = gl_FragColor * e_colourident;\n" + "}\n" + "#endif\n" + }, + {QR_OPENGL, 100, "drawflat_wall", + "!!cvarv r_floorcolor\n" + "!!cvarv r_wallcolor\n" + "!!permu FOG\n" + "#include \"sys/fog.h\"\n" + "varying vec4 col;\n" + "#ifdef VERTEX_SHADER\n" + "attribute vec3 v_normal;\n" + "attribute vec2 v_lmcoord;\n" + "varying vec2 lm;\n" + "uniform vec3 cvar_r_wallcolor;\n" + "uniform vec3 cvar_r_floorcolor;\n" + "uniform vec4 e_lmscale;\n" + + "void main ()\n" + "{\n" + " col = vec4(e_lmscale.rgb/255.0 * ((v_normal.z < 0.73)?cvar_r_wallcolor:cvar_r_floorcolor), e_lmscale.a);\n" + " lm = v_lmcoord;\n" + " gl_Position = ftetransform();\n" + "}\n" + "#endif\n" + + "#ifdef FRAGMENT_SHADER\n" + "uniform sampler2D s_t0;\n" /*tex_lightmap*/ + "varying vec2 lm;\n" + + "void main ()\n" + "{\n" + " gl_FragColor = fog4(col * texture2D(s_t0, lm));\n" "}\n" "#endif\n" }, @@ -4370,10 +4406,10 @@ void Shader_Finish (shader_t *s) if (r_fastsky.ival) s->flags = 0; /*or if its purely a skybox and has missing textures*/ - if (!s->numpasses) - for (i = 0; i < 6; i++) - if (missing_texture.ref == s->skydome->farbox_textures[i].ref) - s->flags = 0; +// if (!s->numpasses) +// for (i = 0; i < 6; i++) +// if (missing_texture.ref == s->skydome->farbox_textures[i].ref) +// s->flags = 0; if (!(s->flags & SHADER_SKY)) { Shader_Reset(s); diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index 0618dd84f..85f4daae5 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -468,7 +468,7 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name), float ver) Con_DPrintf("Anisotropic filter extension found (%dx max).\n",gl_config.ext_texture_filter_anisotropic); } - if (GL_CheckExtension("GL_ARB_texture_non_power_of_two")) + if (GL_CheckExtension("GL_ARB_texture_non_power_of_two") || GL_CheckExtension("GL_OES_texture_npot")) gl_config.arb_texture_non_power_of_two = true; // if (GL_CheckExtension("GL_SGIS_generate_mipmap")) //a suprising number of implementations have this broken. // gl_config.sgis_generate_mipmap = true; @@ -479,7 +479,8 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name), float ver) qglClientActiveTextureARB = (void *) getglext("glClientActiveTexture"); qglSelectTextureSGIS = qglActiveTextureARB; mtexid0 = GL_TEXTURE0_ARB; - qglGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &gl_mtexarbable); + if (!gl_config.nofixedfunc) + qglGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &gl_mtexarbable); } else if (GL_CheckExtension("GL_ARB_multitexture") && !COM_CheckParm("-noamtex")) { //ARB multitexture is the popular choice. @@ -584,7 +585,17 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name), float ver) gl_config.arb_texture_cube_map = GL_CheckExtension("GL_ARB_texture_cube_map"); /*vbos*/ - if (GL_CheckExtension("GL_ARB_vertex_buffer_object")) + if (gl_config.gles && gl_config.glversion >= 2) + { + qglGenBuffersARB = (void *)getglext("glGenBuffers"); + qglDeleteBuffersARB = (void *)getglext("glDeleteBuffers"); + qglBindBufferARB = (void *)getglext("glBindBuffer"); + qglBufferDataARB = (void *)getglext("glBufferData"); + qglBufferSubDataARB = (void *)getglext("glBufferSubData"); + qglMapBufferARB = (void *)getglext("glMapBuffer"); + qglUnmapBufferARB = (void *)getglext("glUnmapBuffer"); + } + else if (GL_CheckExtension("GL_ARB_vertex_buffer_object")) { qglGenBuffersARB = (void *)getglext("glGenBuffersARB"); qglDeleteBuffersARB = (void *)getglext("glDeleteBuffersARB"); @@ -799,21 +810,21 @@ static const char *glsl_hdrs[] = "uniform vec4 w_fog;\n" "vec3 fog3(in vec3 regularcolour)" "{" - "float z = gl_FragCoord.z / gl_FragCoord.w;\n" - "float fac = exp2(-(" - "w_fog.w * w_fog.w * " - "z * z * " - "1.442695));\n" + "float z = w_fog.w * gl_FragCoord.z / gl_FragCoord.w;\n" + "#if #include \"cvar/r_fog_exp2\"\n" + "z *= z;\n" + "#endif\n" + "float fac = exp2(-(z * 1.442695));\n" "fac = clamp(fac, 0.0, 1.0);\n" "return mix(w_fog.rgb, regularcolour, fac);\n" "}\n" "vec3 fog3additive(in vec3 regularcolour)" "{" - "float z = gl_FragCoord.z / gl_FragCoord.w;\n" - "float fac = exp2(-(" - "w_fog.w * w_fog.w * " - "z * z * " - "1.442695));\n" + "float z = w_fog.w * gl_FragCoord.z / gl_FragCoord.w;\n" + "#if #include \"cvar/r_fog_exp2\"\n" + "z *= z;\n" + "#endif\n" + "float fac = exp2(-(z * 1.442695));\n" "fac = clamp(fac, 0.0, 1.0);\n" "return regularcolour * fac;\n" "}\n" @@ -823,21 +834,21 @@ static const char *glsl_hdrs[] = "}\n" "vec4 fog4additive(in vec4 regularcolour)" "{" - "float z = gl_FragCoord.z / gl_FragCoord.w;\n" - "float fac = exp2(-(" - "w_fog.w * w_fog.w * " - "z * z * " - "1.442695));\n" + "float z = w_fog.w * gl_FragCoord.z / gl_FragCoord.w;\n" + "#if #include \"cvar/r_fog_exp2\"\n" + "z *= z;\n" + "#endif\n" + "float fac = exp2(-(z * 1.442695));\n" "fac = clamp(fac, 0.0, 1.0);\n" "return regularcolour * vec4(fac, fac, fac, 1.0);\n" "}\n" "vec4 fog4blend(in vec4 regularcolour)" "{" - "float z = gl_FragCoord.z / gl_FragCoord.w;\n" - "float fac = exp2(-(" - "w_fog.w * w_fog.w * " - "z * z * " - "1.442695));\n" + "float z = w_fog.w * gl_FragCoord.z / gl_FragCoord.w;\n" + "#if #include \"cvar/r_fog_exp2\"\n" + "z *= z;\n" + "#endif\n" + "float fac = exp2(-(z * 1.442695));\n" "fac = clamp(fac, 0.0, 1.0);\n" "return regularcolour * vec4(1.0, 1.0, 1.0, fac);\n" "}\n" @@ -900,25 +911,47 @@ qboolean GLSlang_GenerateIncludes(int maxstrings, int *strings, const GLchar *pr incline += 8; incline = COM_ParseOut (incline, incname, sizeof(incname)); - for (i = 0; glsl_hdrs[i]; i += 2) + if (!strncmp(incname, "cvar/", 5)) { - if (!strcmp(incname, glsl_hdrs[i])) + cvar_t *var = Cvar_Get(incname+5, "0", 0, "shader cvars"); + if (var) { - if (!GLSlang_GenerateIncludes(maxstrings, strings, prstrings, length, glsl_hdrs[i+1])) + var->flags |= CVAR_SHADERSYSTEM; + if (!GLSlang_GenerateIncludes(maxstrings, strings, prstrings, length, var->string)) return false; - break; + } + else + { + /*dump something if the cvar doesn't exist*/ + if (*strings == maxstrings) + return false; + prstrings[*strings] = "0"; + length[*strings] = strlen("0"); + *strings += 1; } } - if (!glsl_hdrs[i]) + else { - if (FS_LoadFile(incname, &inc) >= 0) + for (i = 0; glsl_hdrs[i]; i += 2) { - if (!GLSlang_GenerateIncludes(maxstrings, strings, prstrings, length, inc)) + if (!strcmp(incname, glsl_hdrs[i])) { - FS_FreeFile(inc); - return false; + if (!GLSlang_GenerateIncludes(maxstrings, strings, prstrings, length, glsl_hdrs[i+1])) + return false; + break; + } + } + if (!glsl_hdrs[i]) + { + if (FS_LoadFile(incname, &inc) >= 0) + { + if (!GLSlang_GenerateIncludes(maxstrings, strings, prstrings, length, inc)) + { + FS_FreeFile(inc); + return false; + } + FS_FreeFile(inc); } - FS_FreeFile(inc); } } @@ -1357,7 +1390,7 @@ void GL_Init(void *(*getglfunction) (char *name)) Con_Printf ("end of list\n"); } else - Con_Printf ("GL_EXTENSIONS: %i extensions\n", gl_num_extensions); + Con_DPrintf ("GL_EXTENSIONS: %i extensions\n", gl_num_extensions); gl_extensions = NULL; } else diff --git a/engine/gl/glquake.h b/engine/gl/glquake.h index 7de184fc0..1b4a466dc 100644 --- a/engine/gl/glquake.h +++ b/engine/gl/glquake.h @@ -80,6 +80,32 @@ void ClearBounds (vec3_t mins, vec3_t maxs); #elif defined(__MACOSX__) //apple, you suck. #include + #elif defined(NACL) + #include + #define GLclampd GLclampf + #define GLdouble GLfloat + #define GL_CLAMP GL_CLAMP_TO_EDGE + #define GL_POLYGON (Con_Printf("GL_POLYGON was used"),0) + #define GL_QUAD_STRIP (Con_Printf("GL_QUAD_STRIP was used"),0) + #define GL_QUADS (Con_Printf("GL_QUADS was used"),0) + + + #define GL_PROJECTION (Con_Printf("GL_QUADS was used"),0) + #define GL_MODELVIEW (Con_Printf("GL_QUADS was used"),0) + #define GL_CLIP_PLANE0 (Con_Printf("GL_CLIP_PLANE0 was used"),0) + #define GL_MODULATE (Con_Printf("GL_MODULATE was used"),0) + #define GL_FLAT (Con_Printf("GL_FLAT was used"),0) + #define GL_SMOOTH (Con_Printf("GL_SMOOTH was used"),0) + #define GL_DECAL (Con_Printf("GL_DECAL was used"),0) + #define GL_ADD (Con_Printf("GL_ADD was used"),0) + #define GL_FILL (Con_Printf("GL_FILL was used"),0) + #define GL_TEXTURE_ENV (Con_Printf("GL_TEXTURE_ENV was used"),0) + #define GL_TEXTURE_ENV_MODE (Con_Printf("GL_TEXTURE_ENV_MODE was used"),0) + #define GL_COLOR_ARRAY (Con_Printf("GL_COLOR_ARRAY was used"),0) + #define GL_VERTEX_ARRAY (Con_Printf("GL_VERTEX_ARRAY was used"),0) + #define GL_TEXTURE_COORD_ARRAY (Con_Printf("GL_TEXTURE_COORD_ARRAY was used"),0) + + #else #include #endif @@ -240,7 +266,8 @@ extern vec3_t r_entorigin; extern entity_t *currententity; extern int r_visframecount; // ??? what difs? extern int r_framecount; -extern mplane_t frustum[4]; +#define FRUSTUMPLANES 5 +extern mplane_t frustum[FRUSTUMPLANES]; extern float r_wateralphaval; extern qboolean r_loadbumpmapping; @@ -384,6 +411,7 @@ void R_SaveRTLights_f(void); #ifdef GLQUAKE void GL_DrawHeightmapModel (batch_t **batch, entity_t *e); qboolean GL_LoadHeightmapModel (model_t *mod, void *buffer); +void HeightMap_Purge(model_t *mod); #endif //doom @@ -844,7 +872,11 @@ void GL_SelectProgram(int program); #ifdef _DEBUG +#ifdef __GNUC__ +#define checkglerror() do {int i=qglGetError(); if (i) Sys_Printf("GL Error %i detected at line %s:%i (caller %p)\n", i, __FILE__, __LINE__, __builtin_return_address(0));}while(0) +#else #define checkglerror() do {int i=qglGetError(); if (i) Con_Printf("GL Error %i detected at line %s:%i\n", i, __FILE__, __LINE__);}while(0) +#endif #else #define checkglerror() #endif diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 0f896d05a..a573b7ad3 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -130,6 +130,8 @@ enum SBITS_MASK_BLUE = 0x00040000, SBITS_MASK_ALPHA = 0x00080000, #define SBITS_MASK_BITS 0x000f0000 + + SBITS_TRUFORM = 0x00100000, }; diff --git a/engine/http/ftpclient.c b/engine/http/ftpclient.c index 97d7e6f4c..afa4be879 100644 --- a/engine/http/ftpclient.c +++ b/engine/http/ftpclient.c @@ -48,7 +48,7 @@ FTPclientconn_t *FTP_CreateConnection(char *addy) if ((con->controlsock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { - Sys_Error ("FTP_UDP_OpenSocket: socket: %s\n", strerror(qerrno)); + Sys_Error ("FTP_CreateConnection: socket: %s\n", strerror(qerrno)); } @@ -158,14 +158,14 @@ int FTP_CL_makeconnectsocket(char *ftpdest) if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { - IWebWarnPrintf ("FTP_UDP_OpenSocket: socket: %s", strerror(qerrno)); + IWebWarnPrintf ("FTP_CL_makeconnectsocket: socket: %s", strerror(qerrno)); return INVALID_SOCKET; } if (ioctlsocket (sock, FIONBIO, &_true) == -1) { closesocket(sock); - IWebWarnPrintf ("FTTP_UDP_OpenSocket: ioctl FIONBIO: %s", strerror(qerrno)); + IWebWarnPrintf ("FTP_CL_makeconnectsocket: ioctl FIONBIO: %s", strerror(qerrno)); return INVALID_SOCKET; } @@ -179,7 +179,7 @@ int FTP_CL_makeconnectsocket(char *ftpdest) { closesocket(sock); - IWebWarnPrintf ("FTTP_UDP_OpenSocket: bind: %s", strerror(qerrno)); + IWebWarnPrintf ("FTP_CL_makeconnectsocket: bind: %s", strerror(qerrno)); return INVALID_SOCKET; } @@ -191,7 +191,7 @@ int FTP_CL_makeconnectsocket(char *ftpdest) /* { closesocket(sock); - Con_Printf ("FTTP_UDP_OpenSocket: ioctl FIONBIO: %s", strerror(qerrno)); + Con_Printf ("FTP_CL_makeconnectsocket: ioctl FIONBIO: %s", strerror(qerrno)); return INVALID_SOCKET; } */ diff --git a/engine/http/ftpserver.c b/engine/http/ftpserver.c index 929055d74..4ee0a476a 100644 --- a/engine/http/ftpserver.c +++ b/engine/http/ftpserver.c @@ -485,12 +485,12 @@ iwboolean FTP_ServerThinkForConnection(FTPclient_t *cl) if ((cl->datasock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { - Sys_Error ("FTP_UDP_OpenSocket: socket: %s", strerror(qerrno)); + Sys_Error ("FTP_ServerThinkForConnection: socket: %s", strerror(qerrno)); } if (ioctlsocket (cl->datasock, FIONBIO, (u_long *)&_true) == -1) { - Sys_Error ("FTTP_UDP_OpenSocket: ioctl FIONBIO: %s", strerror(qerrno)); + Sys_Error ("FTP_ServerThinkForConnection: ioctl FIONBIO: %s", strerror(qerrno)); } from.sin_family = AF_INET; diff --git a/engine/http/httpserver.c b/engine/http/httpserver.c index 092b70cf7..cd69600ed 100644 --- a/engine/http/httpserver.c +++ b/engine/http/httpserver.c @@ -322,7 +322,7 @@ cont: if (HTTPmarkup == 3 && !hostspecified) //1.1 requires the host to be specified... we ca,just ignore it as we're not routing or imitating two servers. (for complience we need to encourage the client to send - does nothing for compatability or anything, just compliance to spec. not always the same thing) { - msg = "HTTP/1.1 400 Bad Request\r\n" "Content-Type: text/plain\r\n" "Content-Length: 69\r\n" "Server: "FULLENGINENAME"/0\r\n" "\r\n" "400 Bad Request\r\nYour client failed to provide the host header line"; + msg = "HTTP/1.1 400 Bad Request\r\n" /*"Content-Type: application/octet-stream\r\n"*/ "Content-Length: 69\r\n" "Server: "FULLENGINENAME"/0\r\n" "\r\n" "400 Bad Request\r\nYour client failed to provide the host header line"; ammount = strlen(msg); ExpandOutBuffer(cl, ammount, true); diff --git a/engine/nacl/fs_ppapi.c b/engine/nacl/fs_ppapi.c new file mode 100644 index 000000000..b477b610e --- /dev/null +++ b/engine/nacl/fs_ppapi.c @@ -0,0 +1,858 @@ +#include "quakedef.h" +#include "fs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern PPB_FileIO *ppb_fileio; +extern PPB_FileRef *ppb_fileref; +extern PPB_FileSystem *ppb_filesystem; +extern PPB_Core *ppb_core; +extern PPB_URLLoader *urlloader; +extern PPB_URLRequestInfo *urlrequestinfo; +extern PPB_URLResponseInfo *urlresponseinfo; +extern PPB_Var *ppb_var_interface; +extern PP_Instance pp_instance; + +#define GOOGLE_DONT_KNOW_HOW_TO_CREATE_USABLE_APIS +#ifdef GOOGLE_DONT_KNOW_HOW_TO_CREATE_USABLE_APIS +//the pepper api is flawed. deeply flawed. +//1: api calls (including various gl calls) may only be made on the main thread). +//2: the main thread may not use non-asyncronous calls. +//the recommendation to get around this is to run everything on a worker thread, but to make calls to the main thread to do the actual call, then signal the worker thread to wake up again in the main thread's callback. +//which is impractical when you have 80+ different sorts of performance-dependant gl calls. +//I can't easily put things requiring file access on another thread, if only because it would make alias/exec console commands non-synchronous +//to get around this, I instead make my own memory-only 'filesystem', populating it at startup with downloads. I *really* hope your browser/server are set to enable file caching. +//at some point I'll periodically write the data back to a persistant store/reload it at startup so saved games/configs can work + +#define FSPPAPI_OpenTemp FS_OpenTemp +#define VFSPPAPI_Open VFSOS_Open + +typedef struct mfchunk_s +{ + struct mfchunk_s *prev; + struct mfchunk_s *next; + unsigned long startpos; + unsigned int len; + char data[64]; +} mfchunk_t; +typedef struct mfile_s +{ + /*chunks can be trimmed only when there's no refs*/ + char name[MAX_QPATH]; + int refs; + int unlinked:1; + unsigned long length; + + mfchunk_t *chunkhead; + mfchunk_t *chunktail; + + struct mfile_s *prev; + struct mfile_s *next; +} mfile_t; +mfile_t *mfiles; + +typedef struct +{ + vfsfile_t funcs; + + mfile_t *file; + unsigned long offset; + mfchunk_t *cchunk; +} vfsmfile_t; + +typedef struct +{ + PP_Resource dl; + char buffer[65536]; + char *fname; + vfsfile_t *out; +} dlfile_t; + +static int activedls; +static int availdlslots = 2; +void readfinished(void* user_data, int32_t result) +{ + dlfile_t *f = user_data; + /*if there was a prior request that didn't finish yet...*/ + struct PP_CompletionCallback ccb = {readfinished, f, PP_COMPLETIONCALLBACK_FLAG_OPTIONAL}; + +// Sys_Printf("lastresult: %i\n", result); + + if (result == PP_OK) + { + Sys_Printf("%s completed\n", f->fname); + if (f->out) + VFS_CLOSE(f->out); + //ppb_core->ReleaseResource(f->dl); + activedls--; + availdlslots++; + free(f); + return; + } + + for (; result > 0; result = urlloader->ReadResponseBody(f->dl, f->buffer, sizeof(f->buffer), ccb)) + { + if (!f->out) + { + Sys_Printf("Downloading %s\n", f->fname); + f->out = VFSOS_Open(f->fname, "wb"); + } + +// Sys_Printf("write: %i\n", result); + VFS_WRITE(f->out, f->buffer, result); + } + +// Sys_Printf("result: %i\n", result); + if (result != PP_OK_COMPLETIONPENDING) + { + Sys_Printf("file %s failed or something\n", f->fname); + if (f->out) + VFS_CLOSE(f->out); + ppb_core->ReleaseResource(f->dl); + activedls--; + availdlslots++; + free(f); + } +} +void dlstarted(void* user_data, int32_t result) +{ + dlfile_t *f = user_data; + + struct PP_CompletionCallback ccb = {readfinished, f, PP_COMPLETIONCALLBACK_FLAG_OPTIONAL}; + readfinished(user_data, urlloader->ReadResponseBody(f->dl, f->buffer, sizeof(f->buffer), ccb)); +} +qboolean FSPPAPI_Init(int *fileid) +{ + dlfile_t *dlf; + PP_Resource dlri; + static char *dlnames[] = + { + "id1/pak0.pak", +// "id1/gfx/conback.tga", +// "id1/gfx/conchars.tga", + "id1/pak1.pak", +// "id1/bigass1.dem", +// "id1/overkill.qwd", + "id1/pak2.pak", + "id1/pak3.pak", + "id1/pak4.pak", + NULL + }; + if (availdlslots) + { + if (!dlnames[*fileid]) + { + if (!activedls) + return true; /*engine has all the content it needs*/ + return false; /*still downloading something, don't let it continue just yet*/ + } + + dlf = malloc(sizeof(*dlf)); + if (!dlf) + return false; + dlf->out = NULL; + + activedls++; + availdlslots--; + dlf->fname = dlnames[*fileid]; + *fileid+=1; + dlf->dl = urlloader->Create(pp_instance); + dlri = urlrequestinfo->Create(pp_instance); + urlrequestinfo->SetProperty(dlri, PP_URLREQUESTPROPERTY_URL, ppb_var_interface->VarFromUtf8(dlf->fname, strlen(dlf->fname))); + + struct PP_CompletionCallback ccb = {dlstarted, dlf, PP_COMPLETIONCALLBACK_FLAG_NONE}; + urlloader->Open(dlf->dl, dlri, ccb); + ppb_core->ReleaseResource(dlri); + } + return false; +} + +static int preparechunk(vfsmfile_t *f, int bytes, void **data) +{ + int sz; + mfchunk_t *cnk; + if (!bytes) + { + *data = 0; + return 0; + } + + if (!f->cchunk) + cnk = f->file->chunkhead; + else + { + cnk = f->cchunk; + //rewind through the chunks + while (cnk->startpos > f->offset) + cnk = cnk->prev; + } + //find the chunk that contains our start offset + while (!cnk || cnk->startpos+cnk->len <= f->offset) + { + if (!cnk) + { + sz = (bytes + sizeof(*cnk) - sizeof(cnk->data) + 4095) & ~4095; + if (sz < 65536) + sz = 65536; + cnk = malloc(sz); + memset(cnk, 0xcc, sz); + if (!cnk) + { + *data = 0; + return 0; + } + cnk->len = (sz + sizeof(cnk->data) - sizeof(*cnk)); + cnk->next = NULL; + if (f->file->chunktail) + { + cnk->prev = f->file->chunktail; + cnk->prev->next = cnk; + cnk->startpos = cnk->prev->startpos + cnk->prev->len; + } + else + { + f->file->chunkhead = cnk; + cnk->prev = NULL; + cnk->startpos = 0; + } +// Sys_Printf("Allocated chunk %p: %u-%u\n", cnk, cnk->startpos, cnk->startpos + cnk->len); + f->file->chunktail = cnk; + } + else + cnk = cnk->next; + } + +// Sys_Printf("Returning offset %p, %i\n", cnk, (f->offset - cnk->startpos)); +// Sys_Printf("%u %u\n", f->offset, cnk->startpos); + + *data = cnk->data + (f->offset - cnk->startpos); + f->cchunk = cnk; + + sz = cnk->startpos + cnk->len - f->offset; + if (sz > bytes) + { +// Sys_Printf("Returning len %u\n", bytes); + return bytes; + } +// Sys_Printf("Returning len %u\n", sz); + return sz; + +} + +static int VFSMEM_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread) +{ + int total = 0; + int chunklen; + void *chunkdat; + vfsmfile_t *f = (vfsmfile_t*)file; + + if (bytestoread > f->file->length - f->offset) + bytestoread = f->file->length - f->offset; + + while ((chunklen = preparechunk(f, bytestoread, &chunkdat)) > 0) + { +// Sys_Printf("Read %i at %u\n", chunklen, f->offset); + memcpy(buffer, chunkdat, chunklen); + buffer = (char*)buffer + chunklen; + bytestoread -= chunklen; + total += chunklen; + f->offset += chunklen; + } + +// Sys_Printf("%s", (char*)buffer-total); + return total; +} +static int VFSMEM_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread) +{ + int total = 0; + int chunklen; + void *chunkdat; + vfsmfile_t *f = (vfsmfile_t*)file; + + while ((chunklen = preparechunk(f, bytestoread, &chunkdat)) > 0) + { +// Sys_Printf("Write %i at %u\n", chunklen, f->offset); + memcpy(chunkdat, buffer, chunklen); + buffer = (char*)buffer + chunklen; + bytestoread -= chunklen; + total += chunklen; + f->offset += chunklen; + } + + if (f->file->length < f->offset) + f->file->length = f->offset; + +// Sys_Printf("written: %i, file is now at %i\n", total, f->offset); + return total; +} +static qboolean VFSMEM_Seek (struct vfsfile_s *file, unsigned long pos) +{ + vfsmfile_t *f = (vfsmfile_t*)file; + f->offset = pos; + return true; +} +static unsigned long VFSMEM_Tell (struct vfsfile_s *file) +{ + vfsmfile_t *f = (vfsmfile_t*)file; + return f->offset; +} +static unsigned long VFSMEM_GetSize (struct vfsfile_s *file) +{ + vfsmfile_t *f = (vfsmfile_t*)file; + return f->file->length; +} +static void VFSMEM_Close(vfsfile_t *file) +{ + vfsmfile_t *f = (vfsmfile_t*)file; + f->file->refs -= 1; + if (!f->file->refs) + { + if (f->file->unlinked) + { + mfchunk_t *cnk; + while (f->file->chunkhead) + { + cnk = f->file->chunkhead->next; + free(f->file->chunkhead); + f->file->chunkhead = cnk; + } + free(f->file); + } + } + free(f); +} +static void VFSMEM_Flush(struct vfsfile_s *file) +{ +// vfsmfile_t *f = (vfsmfile_t*)file; +} + + +vfsfile_t *FSPPAPI_OpenTemp(void) +{ + /*create a file which is already unlinked*/ + mfile_t *f; + vfsmfile_t *r; + f = malloc(sizeof(*f)); + if (!f) + return NULL; + + strcpy(f->name, "some temp file"); + f->refs = 0; + f->unlinked = true; + f->length = 0; + f->next = NULL; + f->prev = NULL; + f->chunkhead = NULL; + f->chunktail = NULL; + + r = malloc(sizeof(*r)); + if (!r) + return NULL; + r->file = f; + r->offset = 0; + r->cchunk = NULL; + f->refs++; + + r->funcs.ReadBytes = VFSMEM_ReadBytes; + r->funcs.WriteBytes = VFSMEM_WriteBytes; + r->funcs.Seek = VFSMEM_Seek; + r->funcs.Tell = VFSMEM_Tell; + r->funcs.GetLen = VFSMEM_GetSize; + r->funcs.Close = VFSMEM_Close; + r->funcs.Flush = VFSMEM_Flush; + + return &r->funcs; +} + +vfsfile_t *VFSPPAPI_Open(const char *osname, const char *mode) +{ + mfile_t *f; + vfsmfile_t *r; + + if (strlen(osname) >= sizeof(f->name)) + return NULL; + + for (f = mfiles; f; f = f->next) + { + if (!strcmp(f->name, osname)) + break; + } + if (!f && (*mode == 'w' || *mode == 'a')) + { + f = malloc(sizeof(*f)); + if (f) + { + strcpy(f->name, osname); + f->refs = 0; + f->unlinked = false; + f->length = 0; + f->next = mfiles; + f->prev = NULL; + if (mfiles) + mfiles->prev = f; + mfiles = f; + f->chunkhead = NULL; + f->chunktail = NULL; + } + } + if (!f) + return NULL; + + r = malloc(sizeof(*r)); + if (!r) + return NULL; + r->file = f; + r->offset = 0; + r->cchunk = NULL; + f->refs++; + + r->funcs.ReadBytes = VFSMEM_ReadBytes; + r->funcs.WriteBytes = VFSMEM_WriteBytes; + r->funcs.Seek = VFSMEM_Seek; + r->funcs.Tell = VFSMEM_Tell; + r->funcs.GetLen = VFSMEM_GetSize; + r->funcs.Close = VFSMEM_Close; + r->funcs.Flush = VFSMEM_Flush; + + return &r->funcs; +} + +static vfsfile_t *FSPPAPI_OpenVFS(void *handle, flocation_t *loc, const char *mode) +{ + char diskname[MAX_OSPATH]; + + //path is already cleaned, as anything that gets a valid loc needs cleaning up first. + + snprintf(diskname, sizeof(diskname), "%s/%s", (char*)handle, loc->rawname); + + return VFSPPAPI_Open(diskname, mode); +} + +static void FSPPAPI_PrintPath(void *handle) +{ + Con_Printf("%s\n", (char*)handle); +} +static void FSPPAPI_ClosePath(void *handle) +{ + Z_Free(handle); +} +static int FSPPAPI_RebuildFSHash(const char *filename, int filesize, void *data) +{ + if (filename[strlen(filename)-1] == '/') + { //this is actually a directory + + char childpath[256]; + Q_snprintfz(childpath, sizeof(childpath), "%s*", filename); + Sys_EnumerateFiles((char*)data, childpath, FSPPAPI_RebuildFSHash, data); + return true; + } + if (!Hash_GetInsensative(&filesystemhash, filename)) + { + bucket_t *bucket = (bucket_t*)BZ_Malloc(sizeof(bucket_t) + strlen(filename)+1); + strcpy((char *)(bucket+1), filename); +//#ifdef _WIN32 +// Q_strlwr((char *)(bucket+1)); +//#endif + Hash_AddInsensative(&filesystemhash, (char *)(bucket+1), data, bucket); + + fs_hash_files++; + } + else + fs_hash_dups++; + return true; +} +static void FSPPAPI_BuildHash(void *handle) +{ + Sys_EnumerateFiles(handle, "*", FSPPAPI_RebuildFSHash, handle); +} + +/* +void debugfs(void) +{ + static qboolean firstrun = true; + int len; + FILE *f; + vfsfile_t *v; + char *buf; + if (!firstrun) + return; + firstrun = false; + f = fopen("C:/Games/Quake/fte_trunk/pub/id1/pak0.pak", "rb"); + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, 0, SEEK_SET); + buf = malloc(len); + fread(buf, 1, len, f); + fclose(f); + v = VFSPPAPI_Open("C:\\Games\\Quake/id1/pak0.pak", "wb"); + VFS_WRITE(v, buf, len); + free(buf); + VFS_CLOSE(v); +} +*/ + +static qboolean FSPPAPI_FLocate(void *handle, flocation_t *loc, const char *filename, void *hashedresult) +{ + int len; + char netpath[MAX_OSPATH]; + + if (hashedresult && (void *)hashedresult != handle) + return false; + +/* + if (!static_registered) + { // if not a registered version, don't ever go beyond base + if ( strchr (filename, '/') || strchr (filename,'\\')) + continue; + } +*/ + +// check a file in the directory tree + snprintf (netpath, sizeof(netpath)-1, "%s/%s",(char*)handle, filename); + + { + vfsfile_t *f = VFSPPAPI_Open(netpath, "rb"); + if (!f) + return false; + len = VFS_GETLEN(f); + VFS_CLOSE(f); + } + + if (loc) + { + loc->len = len; + loc->offset = 0; + loc->index = 0; + Q_strncpyz(loc->rawname, filename, sizeof(loc->rawname)); + } + + return true; +} +static void FSPPAPI_ReadFile(void *handle, flocation_t *loc, char *buffer) +{ + vfsfile_t *f; + size_t result; + + f = VFSPPAPI_Open(loc->rawname, "rb"); + if (!f) //err... + return; + VFS_SEEK(f, loc->offset); + result = VFS_READ(f, buffer, loc->len); + + if (result != loc->len) + Con_Printf("FSPPAPI_ReadFile() fread: Filename: %s, expected %i, result was %u\n",loc->rawname,loc->len,(unsigned int)result); + + VFS_CLOSE(f); +} +static int FSPPAPI_EnumerateFiles (void *handle, const char *match, int (*func)(const char *, int, void *), void *parm) +{ + return Sys_EnumerateFiles(handle, match, func, parm); +} + +searchpathfuncs_t osfilefuncs = { + FSPPAPI_PrintPath, + FSPPAPI_ClosePath, + FSPPAPI_BuildHash, + FSPPAPI_FLocate, + FSPPAPI_ReadFile, + FSPPAPI_EnumerateFiles, + NULL, + NULL, + FSPPAPI_OpenVFS +}; + +#else +#define FSPPAPI_OpenTemp FS_OpenTemp +#define VFSPPAPI_Open VFSOS_Open + +extern PPB_FileIO *ppb_fileio; +extern PPB_FileRef *ppb_fileref; +extern PPB_FileSystem *ppb_filesystem; +extern PPB_Core *ppb_core; +extern PP_Instance pp_instance; +static PP_Resource mainfilesystem; + + +struct PP_CompletionCallback nullccb; + +void FSPPAPI_Init(void) +{ + mainfilesystem = ppb_filesystem->Create(pp_instance, PP_FILESYSTEMTYPE_LOCALPERSISTENT); + ppb_filesystem->Open(mainfilesystem, 100*1024*1024, nullccb); +} + +typedef struct { + vfsfile_t funcs; + PP_Resource handle; + int64_t offset; +} vfsppapifile_t; +static int VFSPPAPI_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread) +{ + int res; + vfsppapifile_t *intfile = (vfsppapifile_t*)file; + + res = ppb_fileio->Read(intfile->handle, intfile->offset, buffer, bytestoread, nullccb); + if (res > 0) + intfile->offset += res; + return res; +} +static int VFSPPAPI_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestoread) +{ + int res; + vfsppapifile_t *intfile = (vfsppapifile_t*)file; + res = ppb_fileio->Write(intfile->handle, intfile->offset, buffer, bytestoread, nullccb); + if (res > 0) + intfile->offset += res; + return res; +} +static qboolean VFSPPAPI_Seek (struct vfsfile_s *file, unsigned long pos) +{ + vfsppapifile_t *intfile = (vfsppapifile_t*)file; + intfile->offset = pos; + return true; +} +static unsigned long VFSPPAPI_Tell (struct vfsfile_s *file) +{ + vfsppapifile_t *intfile = (vfsppapifile_t*)file; + return intfile->offset; +} +static void VFSPPAPI_Flush(struct vfsfile_s *file) +{ + vfsppapifile_t *intfile = (vfsppapifile_t*)file; + ppb_fileio->Flush(intfile->handle, nullccb); +} +static unsigned long VFSPPAPI_GetSize (struct vfsfile_s *file) +{ + vfsppapifile_t *intfile = (vfsppapifile_t*)file; + struct PP_FileInfo fileinfo; + + fileinfo.size = 0; + ppb_fileio->Query(intfile->handle, &fileinfo, nullccb); + return fileinfo.size; +} +static void VFSPPAPI_Close(vfsfile_t *file) +{ + vfsppapifile_t *intfile = (vfsppapifile_t*)file; + ppb_fileio->Close(intfile->handle); + ppb_core->ReleaseResource(intfile->handle); + Z_Free(file); +} + +#ifdef _WIN32 +static void VFSPPAPI_CloseTemp(vfsfile_t *file) +{ + vfsppapifile_t *intfile = (vfsppapifile_t*)file; + char *fname = (char*)(intfile+1); + ppb_fileio->Close(intfile->handle); + ppb_core->ReleaseResource(intfile->handle); + /*FIXME: add the remove somewhere*/ +// _unlink(fname); + Z_Free(file); +} +#endif + +vfsfile_t *FSPPAPI_OpenTemp(void) +{ + return NULL; +#if 0 + FILE *f; + vfsppapifile_t *file; + + f = tmpfile(); + if (!f) + return NULL; + + file = Z_Malloc(sizeof(vfsppapifile_t)); + file->funcs.Close = VFSPPAPI_Close; + file->funcs.ReadBytes = VFSPPAPI_ReadBytes; + file->funcs.WriteBytes = VFSPPAPI_WriteBytes; + file->funcs.Seek = VFSPPAPI_Seek; + file->funcs.Tell = VFSPPAPI_Tell; + file->funcs.GetLen = VFSPPAPI_GetSize; + file->funcs.Flush = VFSPPAPI_Flush; + file->handle = f; + + return (vfsfile_t*)file; +#endif +} + +vfsfile_t *VFSPPAPI_Open(const char *osname, const char *mode) +{ + int e; + PP_Resource f; + PP_Resource fsf; + vfsppapifile_t *file; + qboolean read = !!strchr(mode, 'r'); + qboolean write = !!strchr(mode, 'w'); + qboolean append = !!strchr(mode, 'a'); + int newmode = 0; + + if (read) + newmode |= PP_FILEOPENFLAG_READ; + if (write) + newmode |= PP_FILEOPENFLAG_WRITE|PP_FILEOPENFLAG_TRUNCATE|PP_FILEOPENFLAG_CREATE; + if (append) + newmode |= PP_FILEOPENFLAG_WRITE|PP_FILEOPENFLAG_CREATE; + + /*should we support w+ or r+ */ + + fsf = ppb_fileref->Create(mainfilesystem, osname); + f = ppb_fileio->Create(pp_instance); + e = ppb_fileio->Open(f, fsf, newmode, nullccb); + ppb_core->ReleaseResource(fsf); + + if (e != PP_OK) + { + Con_Printf("unable to open %s. error %i\n", osname, e); + return NULL; + } + + file = Z_Malloc(sizeof(vfsppapifile_t)); + file->funcs.ReadBytes = strchr(mode, 'r')?VFSPPAPI_ReadBytes:NULL; + file->funcs.WriteBytes = (strchr(mode, 'w')||strchr(mode, 'a'))?VFSPPAPI_WriteBytes:NULL; + file->funcs.Seek = VFSPPAPI_Seek; + file->funcs.Tell = VFSPPAPI_Tell; + file->funcs.GetLen = VFSPPAPI_GetSize; + file->funcs.Close = VFSPPAPI_Close; + file->funcs.Flush = VFSPPAPI_Flush; + file->handle = f; + + if (append) + file->offset = VFSPPAPI_GetSize((vfsfile_t*)file); + else + file->offset = 0; + + return (vfsfile_t*)file; +} + +static vfsfile_t *FSPPAPI_OpenVFS(void *handle, flocation_t *loc, const char *mode) +{ + char diskname[MAX_OSPATH]; + + //path is already cleaned, as anything that gets a valid loc needs cleaning up first. + + snprintf(diskname, sizeof(diskname), "%s/%s", (char*)handle, loc->rawname); + + return VFSPPAPI_Open(diskname, mode); +} + +static void FSPPAPI_PrintPath(void *handle) +{ + Con_Printf("%s\n", (char*)handle); +} +static void FSPPAPI_ClosePath(void *handle) +{ + Z_Free(handle); +} +static int FSPPAPI_RebuildFSHash(const char *filename, int filesize, void *data) +{ + if (filename[strlen(filename)-1] == '/') + { //this is actually a directory + + char childpath[256]; + Q_snprintfz(childpath, sizeof(childpath), "%s*", filename); + Sys_EnumerateFiles((char*)data, childpath, FSPPAPI_RebuildFSHash, data); + return true; + } + if (!Hash_GetInsensative(&filesystemhash, filename)) + { + bucket_t *bucket = (bucket_t*)BZ_Malloc(sizeof(bucket_t) + strlen(filename)+1); + strcpy((char *)(bucket+1), filename); +//#ifdef _WIN32 +// Q_strlwr((char *)(bucket+1)); +//#endif + Hash_AddInsensative(&filesystemhash, (char *)(bucket+1), data, bucket); + + fs_hash_files++; + } + else + fs_hash_dups++; + return true; +} +static void FSPPAPI_BuildHash(void *handle) +{ + Sys_EnumerateFiles(handle, "*", FSPPAPI_RebuildFSHash, handle); +} +static qboolean FSPPAPI_FLocate(void *handle, flocation_t *loc, const char *filename, void *hashedresult) +{ + int len; + char netpath[MAX_OSPATH]; + + Con_Printf("Locate %s\n", filename); + + + if (hashedresult && (void *)hashedresult != handle) + return false; + +/* + if (!static_registered) + { // if not a registered version, don't ever go beyond base + if ( strchr (filename, '/') || strchr (filename,'\\')) + continue; + } +*/ + +// check a file in the directory tree + snprintf (netpath, sizeof(netpath)-1, "%s/%s",(char*)handle, filename); + + { + vfsfile_t *f = VFSPPAPI_Open(netpath, "rb"); + if (!f) + return false; + len = VFS_GETLEN(f); + VFS_CLOSE(f); + } + + if (loc) + { + loc->len = len; + loc->offset = 0; + loc->index = 0; + Q_strncpyz(loc->rawname, filename, sizeof(loc->rawname)); + } + + return true; +} +static void FSPPAPI_ReadFile(void *handle, flocation_t *loc, char *buffer) +{ + vfsfile_t *f; + size_t result; + + f = VFSPPAPI_Open(loc->rawname, "rb"); + if (!f) //err... + return; + VFS_SEEK(f, loc->offset); + result = VFS_READ(f, buffer, loc->len); + + if (result != loc->len) + Con_Printf("FSPPAPI_ReadFile() fread: Filename: %s, expected %i, result was %u\n",loc->rawname,loc->len,(unsigned int)result); + + VFS_CLOSE(f); +} +static int FSPPAPI_EnumerateFiles (void *handle, const char *match, int (*func)(const char *, int, void *), void *parm) +{ + return Sys_EnumerateFiles(handle, match, func, parm); +} + +searchpathfuncs_t osfilefuncs = { + FSPPAPI_PrintPath, + FSPPAPI_ClosePath, + FSPPAPI_BuildHash, + FSPPAPI_FLocate, + FSPPAPI_ReadFile, + FSPPAPI_EnumerateFiles, + NULL, + NULL, + FSPPAPI_OpenVFS +}; +#endif \ No newline at end of file diff --git a/engine/nacl/gl_vidppapi.c b/engine/nacl/gl_vidppapi.c new file mode 100644 index 000000000..d3fa4d877 --- /dev/null +++ b/engine/nacl/gl_vidppapi.c @@ -0,0 +1,277 @@ +#include "quakedef.h" +#include "glquake.h" +#include "ppapi/c/pp_completion_callback.h" +#include "ppapi/gles2/gl2ext_ppapi.h" +#include "ppapi/c/ppb_graphics_3d.h" +#include "ppapi/c/ppb_instance.h" + +extern PPB_GetInterface sys_gbi; +extern PPB_Graphics3D* graphics3d_interface; +extern PP_Instance pp_instance; +static PP_Resource glcontext; +extern PPB_Instance* instance_interface; +int delayedswap = false; + +void swap_callback(void* user_data, int32_t result) +{ +// printf("swap result: %d\n", result); +} +void GL_BeginRendering (void) +{ +} +void GL_EndRendering (void) +{ + delayedswap = true; + glFlush(); +} + +void GL_DoSwap(void) +{ + if (delayedswap) + { + struct PP_CompletionCallback ccb = { swap_callback, NULL, PP_COMPLETIONCALLBACK_FLAG_OPTIONAL}; + graphics3d_interface->SwapBuffers(glcontext, ccb); + delayedswap = false; + } +} + +void GLVID_SetPalette (unsigned char *palette) +{ + qbyte *pal; + unsigned int r,g,b; + int i; + unsigned *table1; + extern qbyte gammatable[256]; + + pal = palette; + table1 = d_8to24rgbtable; + for (i=0 ; i<256 ; i++) + { + r = gammatable[pal[0]]; + g = gammatable[pal[1]]; + b = gammatable[pal[2]]; + pal += 3; + + *table1++ = LittleLong((255<<24) + (r<<0) + (g<<8) + (b<<16)); + } + d_8to24rgbtable[255] &= LittleLong(0xffffff); // 255 is transparent +} + +void GLVID_ShiftPalette (unsigned char *palette) +{ +} + +void *PPAPI_GetGLSymbol(char *symname) +{ + int i; + static struct {char *name; void *ptr;} funcs[] = + { +#define f(n) {#n , n}, + f(glActiveTexture) + f(glAttachShader) + f(glBindAttribLocation) + f(glBindBuffer) + f(glBindFramebuffer) + f(glBindRenderbuffer) + f(glBindTexture) + f(glBlendColor) + f(glBlendEquation) + f(glBlendEquationSeparate) + f(glBlendFunc) + f(glBlendFuncSeparate) + f(glBufferData) + f(glBufferSubData) + f(glCheckFramebufferStatus) + f(glClear) + f(glClearColor) + f(glClearDepthf) + f(glClearStencil) + f(glColorMask) + f(glCompileShader) + f(glCompressedTexImage2D) + f(glCompressedTexSubImage2D) + f(glCopyTexImage2D) + f(glCopyTexSubImage2D) + f(glCreateProgram) + f(glCreateShader) + f(glCullFace) + f(glDeleteBuffers) + f(glDeleteFramebuffers) + f(glDeleteProgram) + f(glDeleteRenderbuffers) + f(glDeleteShader) + f(glDeleteTextures) + f(glDepthFunc) + f(glDepthMask) + f(glDepthRangef) + f(glDetachShader) + f(glDisable) + f(glDisableVertexAttribArray) + f(glDrawArrays) + f(glDrawElements) + f(glEnable) + f(glEnableVertexAttribArray) + f(glFinish) + f(glFlush) + f(glFramebufferRenderbuffer) + f(glFramebufferTexture2D) + f(glFrontFace) + f(glGenBuffers) + f(glGenerateMipmap) + f(glGenFramebuffers) + f(glGenRenderbuffers) + f(glGenTextures) + f(glGetActiveAttrib) + f(glGetActiveUniform) + f(glGetAttachedShaders) + f(glGetAttribLocation) + f(glGetBooleanv) + f(glGetBufferParameteriv) + f(glGetError) + f(glGetFloatv) + f(glGetFramebufferAttachmentParameteriv) + f(glGetIntegerv) + f(glGetProgramiv) + f(glGetProgramInfoLog) + f(glGetRenderbufferParameteriv) + f(glGetShaderiv) + f(glGetShaderInfoLog) + f(glGetShaderPrecisionFormat) + f(glGetShaderSource) + f(glGetString) + f(glGetTexParameterfv) + f(glGetTexParameteriv) + f(glGetUniformfv) + f(glGetUniformiv) + f(glGetUniformLocation) + f(glGetVertexAttribfv) + f(glGetVertexAttribiv) + f(glGetVertexAttribPointerv) + f(glHint) + f(glIsBuffer) + f(glIsEnabled) + f(glIsFramebuffer) + f(glIsProgram) + f(glIsRenderbuffer) + f(glIsShader) + f(glIsTexture) + f(glLineWidth) + f(glLinkProgram) + f(glPixelStorei) + f(glPolygonOffset) + f(glReadPixels) + f(glReleaseShaderCompiler) + f(glRenderbufferStorage) + f(glSampleCoverage) + f(glScissor) + f(glShaderBinary) + f(glShaderSource) + f(glStencilFunc) + f(glStencilFuncSeparate) + f(glStencilMask) + f(glStencilMaskSeparate) + f(glStencilOp) + f(glStencilOpSeparate) + f(glTexImage2D) + f(glTexParameterf) + f(glTexParameterfv) + f(glTexParameteri) + f(glTexParameteriv) + f(glTexSubImage2D) + f(glUniform1f) + f(glUniform1fv) + f(glUniform1i) + f(glUniform1iv) + f(glUniform2f) + f(glUniform2fv) + f(glUniform2i) + f(glUniform2iv) + f(glUniform3f) + f(glUniform3fv) + f(glUniform3i) + f(glUniform3iv) + f(glUniform4f) + f(glUniform4fv) + f(glUniform4i) + f(glUniform4iv) + f(glUniformMatrix2fv) + f(glUniformMatrix3fv) + f(glUniformMatrix4fv) + f(glUseProgram) + f(glValidateProgram) + f(glVertexAttrib1f) + f(glVertexAttrib1fv) + f(glVertexAttrib2f) + f(glVertexAttrib2fv) + f(glVertexAttrib3f) + f(glVertexAttrib3fv) + f(glVertexAttrib4f) + f(glVertexAttrib4fv) + f(glVertexAttribPointer) + f(glViewport) + {NULL} + }; + for (i = 0; funcs[i].name; i++) + { + if (!strcmp(funcs[i].name, symname)) + return funcs[i].ptr; + } + return NULL; +} + +qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) +{ + int32_t attribs[] = {PP_GRAPHICS3DATTRIB_WIDTH, info->width, + PP_GRAPHICS3DATTRIB_HEIGHT, info->height, + PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24, + PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8, + PP_GRAPHICS3DATTRIB_NONE}; + + glcontext = graphics3d_interface->Create(pp_instance, 0, attribs); + + glSetCurrentContextPPAPI(glcontext); + + if (!instance_interface->BindGraphics(pp_instance, glcontext)) + { + Con_Printf("failed to bind context\n"); + return false; + } + + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + GL_EndRendering(); + GL_DoSwap(); + + vid.pixelwidth = info->width; + vid.pixelheight = info->height; + + GLVID_SetPalette (palette); + GL_Init(PPAPI_GetGLSymbol); + vid.recalc_refdef = 1; + + return true; +} + +void GLVID_Shutdown (void) +{ + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + GL_EndRendering(); + GL_DoSwap(); + + glTerminatePPAPI(); +} +void GLVID_DeInit (void) +{ + GLVID_Shutdown(); +} + +void GLVID_Crashed(void); + +void GLVID_Update (vrect_t *rects); + +int GLVID_SetMode (rendererstate_t *info, unsigned char *palette); + +void GLVID_SetCaption(char *caption) +{ +} \ No newline at end of file diff --git a/engine/nacl/snd_ppapi.c b/engine/nacl/snd_ppapi.c new file mode 100644 index 000000000..f78395492 --- /dev/null +++ b/engine/nacl/snd_ppapi.c @@ -0,0 +1,115 @@ +#include "quakedef.h" + +#include +#include +#include +extern PPB_Core *ppb_core; +extern PPB_Audio *audio_interface; +extern PPB_AudioConfig *audioconfig_interface; +extern PP_Instance pp_instance; + +static PPB_Audio_Callback audio_callback; + +extern int GetSoundtime(soundcardinfo_t *sc); + +static void PPAPI_audio_callback(void *sample_buffer, uint32_t len, void *user_data) +{ + soundcardinfo_t *sc = user_data; + unsigned int framesz; + if (sc) + { + int curtime = GetSoundtime(sc); + framesz = sc->sn.numchannels * sc->sn.samplebits/8; + + //might as well dump it directly... + sc->sn.buffer = sample_buffer; + sc->sn.samples = len / (sc->sn.samplebits/8); + S_PaintChannels (sc, curtime + (len / framesz)); + sc->sn.samples = 0; + sc->sn.buffer = NULL; + + sc->snd_sent += len; + } +} + +static void PPAPI_Shutdown(soundcardinfo_t *sc) +{ + audio_interface->StopPlayback((PP_Resource)sc->handle); + ppb_core->ReleaseResource((PP_Resource)sc->handle); +} + +static unsigned int PPAPI_GetDMAPos(soundcardinfo_t *sc) +{ + sc->sn.samplepos = sc->snd_sent / (sc->sn.samplebits/8); + return sc->sn.samplepos; +} + +static void PPAPI_UnlockBuffer(soundcardinfo_t *sc, void *buffer) +{ +} + +static void *PPAPI_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx) +{ + *sampidx = 0; + return sc->sn.buffer; +} + +static void PPAPI_SetUnderWater(soundcardinfo_t *sc, qboolean uw) +{ +} + +static void PPAPI_Submit(soundcardinfo_t *sc, int start, int end) +{ +} + +int PPAPI_InitCard (soundcardinfo_t *sc, int cardnum) +{ + PP_Resource config; + int framecount; + + /*I'm not aware of any limits on the number of 'devices' we can create, but virtual devices on the same physical device are utterly pointless, so don't load more than one*/ + if (cardnum != 0) + return 2; + + /*the docs say only two sample rates are allowed*/ + if (sc->sn.speed <= 44100) + sc->sn.speed = 44100; + else + sc->sn.speed = 48000; + /*we can't choose these two settings*/ + sc->sn.samplebits = 16; + sc->sn.numchannels = 2; + + framecount = audioconfig_interface->RecommendSampleFrameCount(sc->sn.speed, 2048); + + /*the callback paints directly into the caller's buffer, so we don't need a separate 'dma' buffer*/ + sc->selfpainting = true; + sc->sn.samples = 0; /*framecount*/ + sc->sn.buffer = NULL; + + sc->snd_sent = 0; + sc->sn.samplepos = 0; + + sc->Submit = PPAPI_Submit; + sc->GetDMAPos = PPAPI_GetDMAPos; + sc->Lock = PPAPI_LockBuffer; + sc->Unlock = PPAPI_UnlockBuffer; + sc->SetWaterDistortion = PPAPI_SetUnderWater; + sc->Shutdown = PPAPI_Shutdown; + + + config = audioconfig_interface->CreateStereo16Bit(pp_instance, sc->sn.speed, framecount); + if (config) + { + sc->handle = (void*)audio_interface->Create(pp_instance, config, PPAPI_audio_callback, sc); + ppb_core->ReleaseResource(config); + if (sc->handle) + { + if (audio_interface->StartPlayback((PP_Resource)sc->handle)) + return 1; + } + } + return 0; +} + +int (*pPPAPI_InitCard) (soundcardinfo_t *sc, int cardnum) = &PPAPI_InitCard; \ No newline at end of file diff --git a/engine/nacl/sys_ppapi.c b/engine/nacl/sys_ppapi.c new file mode 100644 index 000000000..d8ded1c77 --- /dev/null +++ b/engine/nacl/sys_ppapi.c @@ -0,0 +1,614 @@ +#include "quakedef.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +PPB_Core *ppb_core = NULL; +PPB_Graphics3D *graphics3d_interface = NULL; +PPB_Instance *instance_interface = NULL; +PPB_Messaging *ppb_messaging_interface = NULL; +PPB_Var *ppb_var_interface = NULL; +PPB_InputEvent *ppb_inputevent_interface = NULL; +PPB_KeyboardInputEvent *ppb_keyboardinputevent_interface = NULL; +PPB_MouseInputEvent *ppb_mouseinputevent_interface = NULL; +PPB_WheelInputEvent *ppb_wheelinputevent_interface = NULL; +PPB_FileIO *ppb_fileio = NULL; +PPB_FileRef *ppb_fileref = NULL; +PPB_FileSystem *ppb_filesystem = NULL; +PPB_URLLoader *urlloader = NULL; +PPB_URLRequestInfo *urlrequestinfo = NULL; +PPB_URLResponseInfo *urlresponseinfo = NULL; +PPB_Audio *audio_interface = NULL; +PPB_AudioConfig *audioconfig_interface = NULL; +PPB_MouseLock *ppb_mouselock_interface = NULL; +PPB_Fullscreen *ppb_fullscreen_interface = NULL; +PPB_WebSocket *ppb_websocket_interface = NULL;; +PP_Instance pp_instance; +PPB_GetInterface sys_gbi; +static double lasttime; +static qboolean mouselocked; +static qboolean shuttingdown; + +qboolean FSPPAPI_Init(int *filenocookie); + +unsigned short htons(unsigned short a) +{ + union + { + unsigned char c[2]; + unsigned short s; + } u; + u.s = a; + + return u.c[0] | (unsigned short)(u.c[1]<<8); +} +unsigned short ntohs(unsigned short a) +{ + return htons(a); +} +unsigned int htonl(unsigned int a) +{ + union + { + unsigned char c[4]; + unsigned int s; + } u; + u.s = a; + + return u.c[0] | (unsigned int)(u.c[1]<<8) | (unsigned int)(u.c[2]<<16) | (unsigned int)(u.c[3]<<24); +} +unsigned long ntohl(unsigned long a) +{ + return htonl(a); +} + +qboolean isDedicated = false; + + +dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs) +{ + return NULL; +} +void Sys_CloseLibrary(dllhandle_t *lib) +{ +} +void *Sys_GetAddressForName(dllhandle_t *module, const char *exportname) +{ + return NULL; +} +char *Sys_GetNameForAddress(dllhandle_t *module, void *address) +{ + return NULL; +} + +qboolean Sys_RandomBytes(qbyte *string, int len) +{ + return false; +} + +//q2... +void Sys_UnloadGame (void) +{ +} +//q2... +void *Sys_GetGameAPI (void *parms) +{ + return NULL; +} + +qboolean Sys_InitTerminal (void) +{ + return false; +} +void Sys_CloseTerminal (void) +{ +} + +char *Sys_GetClipboard(void) +{ + return NULL; +} +void Sys_CloseClipboard(char *buf) +{ +} +void Sys_SaveClipboard(char *text) +{ +} +void Sys_ServerActivity(void) +{ +} + +char *Sys_ConsoleInput (void) +{ + return NULL; +} +void Sys_SendKeyEvents(void) +{ +} + +void Sys_Init (void) +{ +} +void Sys_Shutdown(void) +{ +} + +//nacl supposedly has no way to implement this (other than us writing a listfile in each directory) +int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, int, void *), void *parm) +{ + return 0; +} + +qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate) +{ + *width = 1024; + *height = 768; + *bpp = 32; + *refreshrate = 60; + return true; + + return false; +} + +// an error will cause the entire program to exit +NORETURN void VARGS Sys_Error (const char *error, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, error); + vsnprintf (string, sizeof(string)-1, error, argptr); + va_end (argptr); + + Sys_Printf("Sys_Error: "); + Sys_Printf("%s", string); + exit(1); +} + +static struct PP_Var CStrToVar(const char* str) +{ + if (ppb_var_interface != NULL) + { + return ppb_var_interface->VarFromUtf8(str, strlen(str)); + } + return PP_MakeUndefined(); +} +void VARGS Sys_Printf (char *fmt, ...) +{ + va_list argptr; + char string[1024]; + + va_start (argptr, fmt); + vsnprintf (string, sizeof(string)-1, fmt, argptr); + va_end (argptr); + + //this stuff generally doesn't even get shown + printf("%s", string); + if (pp_instance) + ppb_messaging_interface->PostMessage(pp_instance, CStrToVar(string)); +} + +void Sys_Quit (void) +{ + Sys_Printf("Sys_Quit\n"); + + shuttingdown = true; +} +void Sys_RecentServer(char *command, char *target, char *title, char *desc) +{ +} + +void Sys_mkdir (char *path) +{ +} +qboolean Sys_remove (char *path) +{ + return false; +} + +#include +static int secbase; +double Sys_DoubleTime(void) +{ + struct timeval tp; + struct timezone tzp; + + gettimeofday(&tp, &tzp); + + if (!secbase) + { + secbase = tp.tv_sec; + return tp.tv_usec/1000000.0; + } + + return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0; +} + +unsigned int Sys_Milliseconds (void) +{ + return Sys_DoubleTime()*1000; +} + +void FrameEvent(void* user_data, int32_t result) +{ + if (shuttingdown) + { + if (!mouselocked && ppb_mouselock_interface) + ppb_mouselock_interface->UnlockMouse(pp_instance); + if (ppb_fullscreen_interface) + ppb_fullscreen_interface->SetFullscreen(pp_instance, PP_FALSE); + Host_Shutdown (); + ppb_inputevent_interface->RequestInputEvents(pp_instance, 0); + shuttingdown = false; + return; + } + if (pp_instance) + { + double newtime = Sys_DoubleTime(); + Host_Frame(newtime - lasttime); + lasttime = newtime; + +// Sys_Printf("Frame %f\n", newtime); + + struct PP_CompletionCallback ccb = {FrameEvent, user_data, PP_COMPLETIONCALLBACK_FLAG_NONE}; + ppb_core->CallOnMainThread(0, ccb, 0); + } +} +void startquake(void) +{ + const char *args [] = + { + "ftedroid", + "", + "" + }; + quakeparms_t parms; + parms.basedir = ""; /*filled in later*/ + parms.argc = 1; + parms.argv = args; +//FIXME: do something with the embed arguments + parms.memsize = 16*1024*1024; + parms.membase = malloc(parms.memsize); + if (!parms.membase) + { + Sys_Printf("Unable to alloc heap\n"); + return; + } + + Sys_Printf("Starting up\n"); + + COM_InitArgv(parms.argc, parms.argv); + TL_InitLanguages(); + #ifdef SERVERONLY + SV_Init(&parms); + #else + Host_Init(&parms); + #endif + + lasttime = Sys_DoubleTime(); + + FrameEvent(NULL, 0); +} + +void trystartquake(void* user_data, int32_t result) +{ + if (FSPPAPI_Init(&result)) + startquake(); + else + { + struct PP_CompletionCallback ccb = {trystartquake, user_data, PP_COMPLETIONCALLBACK_FLAG_NONE}; + ppb_core->CallOnMainThread(100, ccb, result); + } +} + +static PP_Bool Instance_DidCreate(PP_Instance instance, + uint32_t argc, + const char* argn[], + const char* argv[]) +{ + pp_instance = instance; + +//FIXME: do something with the embed arguments + + ppb_inputevent_interface->RequestInputEvents(pp_instance, PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_KEYBOARD | PP_INPUTEVENT_CLASS_WHEEL); + + trystartquake(NULL, 0); + + return PP_TRUE; +} + +static void cb_mouselocked(void* user_data, int32_t result) +{ + if (result == PP_OK) + { + mouselocked = true; + } +} +static void ppp_mouseunlocked(PP_Instance instance) +{ + mouselocked = false; +} + +unsigned int domkeytoquake(unsigned int code) +{ +#define K_PRINTSCREEN ' ' + unsigned int tab[256] = + { + /* 0*/ 0,0,0,0,0,0,0,0, K_BACKSPACE,K_TAB,0,0,0,K_ENTER,0,0, + /* 16*/ K_SHIFT,K_CTRL,K_ALT,K_PAUSE,K_CAPSLOCK,0,0,0, 0,0,0,K_ESCAPE,0,0,0,0, + /* 32*/ ' ',K_PGUP,K_PGDN,K_END,K_HOME,K_LEFTARROW,K_UPARROW,K_RIGHTARROW, K_DOWNARROW,0,0,0,K_PRINTSCREEN,K_INS,K_DEL,0, + /* 48*/ '0','1','2','3','4','5','6','7', '8','9',0,0,0,0,0,0, + + /* 64*/ 0,'a','b','c','d','e','f','g', 'h','i','j','k','l','m','n','o', + /* 80*/ 'p','q','r','s','t','u','v','w', 'x','y','z',K_LWIN,K_RWIN,K_APP,0,0, + /* 96*/ K_KP_INS,K_KP_END,K_KP_DOWNARROW,K_KP_PGDN,K_KP_LEFTARROW,K_KP_5,K_KP_RIGHTARROW,K_KP_HOME, K_KP_UPARROW,K_KP_PGDN,K_KP_STAR,K_KP_PLUS,0,K_KP_MINUS,K_KP_DEL,K_KP_SLASH, + /*112*/ K_F1,K_F2,K_F3,K_F4,K_F5,K_F6,K_F7,K_F8, K_F9,K_F10,K_F11,K_F12,0,0,0,0, + /*128*/ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + /*144*/ K_KP_NUMLOCK,K_SCRLCK,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + /*160*/ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + /*176*/ 0,0,0,0,0,0,0,0, 0,0,';','=',',','-','.','/', + /*192*/ '\'',0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + /*208*/ 0,0,0,0,0,0,0,0, 0,0,0,'[','\\',']','#','`', + /*224*/ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + /*240*/ 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + }; + if (code >= sizeof(tab)/sizeof(tab[0])) + { + Con_DPrintf("You just pressed key %u, but I don't know what its meant to be\n", code); + return 0; + } + if (!tab[code]) + Con_DPrintf("You just pressed key %u, but I don't know what its meant to be\n", code); + return tab[code]; +} + +void IN_QueueKey(int down, int keycode, int unicode); +void IN_QueueMouse(int act, int ptrid, float x, float y, int button); +void IN_AmmendUnicode(int unicode); +PP_Bool InputEvent_HandleEvent(PP_Instance pp_instance, PP_Resource resource) +{ + extern cvar_t vid_fullscreen; + if (!pp_instance || !host_initialized) + return PP_FALSE; + + switch(ppb_inputevent_interface->GetType(resource)) + { + case PP_INPUTEVENT_TYPE_MOUSEDOWN: + if (vid_fullscreen.ival) + { + if (ppb_fullscreen_interface) + ppb_fullscreen_interface->SetFullscreen(pp_instance, PP_TRUE); + + if (!mouselocked && ppb_mouselock_interface) + { + struct PP_CompletionCallback ccb = {cb_mouselocked, NULL, PP_COMPLETIONCALLBACK_FLAG_NONE}; + int res = ppb_mouselock_interface->LockMouse(pp_instance, ccb); + if (res != PP_OK_COMPLETIONPENDING) + cb_mouselocked(NULL, res); + else + return PP_TRUE; + } + } + IN_QueueMouse(1, 0, 0, 0, ppb_mouseinputevent_interface->GetButton(resource)); + return PP_TRUE; + case PP_INPUTEVENT_TYPE_MOUSEUP: + IN_QueueMouse(2, 0, 0, 0, ppb_mouseinputevent_interface->GetButton(resource)); + return PP_TRUE; + case PP_INPUTEVENT_TYPE_MOUSEMOVE: + { + struct PP_Point p; + if (mouselocked) + { + p = ppb_mouseinputevent_interface->GetMovement(resource); + IN_QueueMouse(3, 0, p.x, p.y, 0); + } + else + { + p = ppb_mouseinputevent_interface->GetPosition(resource); + IN_QueueMouse(0, 0, p.x, p.y, 0); + } + } + return PP_TRUE; + case PP_INPUTEVENT_TYPE_MOUSEENTER: + //we don't really care too much if it leave the window +// Con_Printf("mouseenter\n"); + return PP_TRUE; + case PP_INPUTEVENT_TYPE_MOUSELEAVE: + //we don't really care too much if it leave the window (should throttle framerates perhaps, but that's all) +// Con_Printf("mouseleave\n"); + return PP_TRUE; + case PP_INPUTEVENT_TYPE_WHEEL: + { + struct PP_FloatPoint p; + p = ppb_wheelinputevent_interface->GetTicks(resource); + //BUG: the value is fractional. + while (p.x >= 1) + { + IN_QueueKey(1, K_MWHEELUP, 0); + IN_QueueKey(0, K_MWHEELUP, 0); + p.x--; + } + while (p.x <= -1) + { + IN_QueueKey(1, K_MWHEELDOWN, 0); + IN_QueueKey(0, K_MWHEELDOWN, 0); + p.x++; + } + } + return PP_TRUE; + case PP_INPUTEVENT_TYPE_RAWKEYDOWN: +// Con_Printf("rawkeydown\n"); + return PP_FALSE; + case PP_INPUTEVENT_TYPE_KEYDOWN: + IN_QueueKey(1, domkeytoquake(ppb_keyboardinputevent_interface->GetKeyCode(resource)), 0); + return PP_FALSE; + case PP_INPUTEVENT_TYPE_KEYUP: + IN_QueueKey(0, domkeytoquake(ppb_keyboardinputevent_interface->GetKeyCode(resource)), 0); + return PP_TRUE; + case PP_INPUTEVENT_TYPE_CHAR: + { + const unsigned char *s; + unsigned int c; + unsigned int len; + len = 0; + s = ppb_var_interface->VarToUtf8(ppb_keyboardinputevent_interface->GetCharacterText(resource), &len); + while(len) + { + if (*s & 0x80) + { + if (!(*s & 0x40)) + { + //illegal encoding + c = '?'; + len -= 1; + } + else if (!(*s & 0x20) && (s[1] & 0xc0) == 0x80) + { + c = ((s[0] & 0x1f)<<6) | ((s[1] & 0x3f)<<0); + if (c < (1<<7)) + c = '?'; + len -= 2; + } + else if (!(*s & 0x10) && (s[1] & 0xc0) == 0x80 && (s[2] & 0xc0) == 0x80) + { + c = ((s[0] & 0x0f)<<12) | ((s[1] & 0x3f)<<6) | ((s[2] & 0x3f)<<0); + if (c < (1<<13)) + c = '?'; + len -= 3; + } + else if (!(*s & 0x08) && (s[1] & 0xc0) == 0x80 && (s[2] & 0xc0) == 0x80 && (s[3] & 0xc0) == 0x80) + { + c = ((s[0] & 0x07)<<18) | ((s[1] & 0x3f)<<12) | ((s[2] & 0x3f)<<6) | ((s[3] & 0x3f)<<0); + if (c < (1<<19)) + c = '?'; + len -= 4; + } + else + { + //too lazy to handle that encoding + c = '?'; + len -= 1; + } + } + else + { + c = *s; + len--; + } + + IN_AmmendUnicode(c); + } + } + return PP_TRUE; + case PP_INPUTEVENT_TYPE_CONTEXTMENU: + //We don't care about the context menu, we just want to be able to right-click. + return PP_TRUE; + default: + Con_Printf("Unknown input event type\n"); + break; + } + return PP_FALSE; +} + +static void Instance_DidDestroy(PP_Instance instance) +{ +} +static void Instance_DidChangeView(PP_Instance instance, PP_Resource view_resource) +{ +} +static void Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) +{ +} +static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance, PP_Resource url_loader) +{ + return PP_FALSE; +} + + +PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id, PPB_GetInterface get_browser) +{ + ppb_core = (PPB_Core*)(get_browser(PPB_CORE_INTERFACE)); + sys_gbi = get_browser; + graphics3d_interface = (PPB_Graphics3D*)(get_browser(PPB_GRAPHICS_3D_INTERFACE)); + ppb_messaging_interface = (PPB_Messaging*)(get_browser(PPB_MESSAGING_INTERFACE)); + ppb_var_interface = (PPB_Var*)(get_browser(PPB_VAR_INTERFACE)); + instance_interface = (PPB_Instance*)(get_browser(PPB_INSTANCE_INTERFACE)); + ppb_inputevent_interface = (PPB_InputEvent*)(get_browser(PPB_INPUT_EVENT_INTERFACE)); + ppb_keyboardinputevent_interface = (PPB_KeyboardInputEvent*)(get_browser(PPB_KEYBOARD_INPUT_EVENT_INTERFACE)); + ppb_mouseinputevent_interface = (PPB_MouseInputEvent*)(get_browser(PPB_MOUSE_INPUT_EVENT_INTERFACE)); + ppb_wheelinputevent_interface = (PPB_WheelInputEvent*)(get_browser(PPB_WHEEL_INPUT_EVENT_INTERFACE)); + ppb_fileio = (PPB_FileIO*)(get_browser(PPB_FILEIO_INTERFACE)); + ppb_fileref = (PPB_FileRef*)(get_browser(PPB_FILEREF_INTERFACE)); + ppb_filesystem = (PPB_FileSystem*)(get_browser(PPB_FILESYSTEM_INTERFACE)); + urlloader = (PPB_URLLoader*)(get_browser(PPB_URLLOADER_INTERFACE )); + urlrequestinfo = (PPB_URLRequestInfo*)(get_browser(PPB_URLREQUESTINFO_INTERFACE)); + urlresponseinfo = (PPB_URLResponseInfo*)(get_browser(PPB_URLRESPONSEINFO_INTERFACE)); + audio_interface = (PPB_Audio*)(get_browser(PPB_AUDIO_INTERFACE)); + audioconfig_interface = (PPB_AudioConfig*)(get_browser(PPB_AUDIO_CONFIG_INTERFACE)); + ppb_mouselock_interface = (PPB_MouseLock*)(get_browser(PPB_MOUSELOCK_INTERFACE)); + ppb_fullscreen_interface = (PPB_Fullscreen*)(get_browser(PPB_FULLSCREEN_INTERFACE)); + ppb_websocket_interface = (PPB_WebSocket*)(get_browser(PPB_WEBSOCKET_INTERFACE)); + + glInitializePPAPI(sys_gbi); + + return PP_OK; +} +PP_EXPORT const void* PPP_GetInterface(const char* interface_name) +{ + if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) + { + static PPP_Instance instance_interface = + { + &Instance_DidCreate, + &Instance_DidDestroy, + &Instance_DidChangeView, + &Instance_DidChangeFocus, + &Instance_HandleDocumentLoad, + }; + return &instance_interface; + } + if (strcmp(interface_name, PPP_INPUT_EVENT_INTERFACE) == 0) + { + static PPP_InputEvent input_event_interface = + { + &InputEvent_HandleEvent + }; + return &input_event_interface; + } + if (strcmp(interface_name, PPP_MOUSELOCK_INTERFACE ) == 0) + { + static PPP_MouseLock mouselock_interface = + { + &ppp_mouseunlocked + }; + return &mouselock_interface; + } + return NULL; +} +PP_EXPORT void PPP_ShutdownModule() +{ +} \ No newline at end of file diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 7a245ccd3..064188428 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -2843,7 +2843,8 @@ PF_ambientsound */ void PF_ambientsound_Internal (float *pos, char *samp, float vol, float attenuation) { - int i, soundnum; + int i, soundnum, j; + sizebuf_t *buf[3] = {&sv.signon, &sv.nqmulticast, &sv.multicast}; // check to see if samp was properly precached for (soundnum=1 ; *sv.strings.sound_precache[soundnum] ; soundnum++) @@ -2861,17 +2862,17 @@ void PF_ambientsound_Internal (float *pos, char *samp, float vol, float attenuat if (soundnum > 255) return; -// add an svc_spawnambient command to the level signon packet - - MSG_WriteByte (&sv.signon,svc_spawnstaticsound); - for (i=0 ; i<3 ; i++) - MSG_WriteCoord(&sv.signon, pos[i]); - - MSG_WriteByte (&sv.signon, soundnum); - - MSG_WriteByte (&sv.signon, vol*255); - MSG_WriteByte (&sv.signon, attenuation*64); - + for (j = 0; j < 3; j++) + { + // add an svc_spawnambient command to the level signon packet + MSG_WriteByte (buf[j],svc_spawnstaticsound); + for (i=0 ; i<3 ; i++) + MSG_WriteCoord(buf[j], pos[i]); + MSG_WriteByte (buf[j], soundnum); + MSG_WriteByte (buf[j], vol*255); + MSG_WriteByte (buf[j], attenuation*64); + } + SV_Multicast(pos, MULTICAST_ALL_R); } static void QCBUILTIN PF_ambientsound (progfuncs_t *prinst, struct globalvars_s *pr_globals) @@ -3329,7 +3330,10 @@ static void QCBUILTIN PF_dropclient (progfuncs_t *prinst, struct globalvars_s *p // so long and thanks for all the fish if (cl->netchan.remote_address.type == NA_LOOPBACK) + { + Cbuf_AddText ("disconnect\n", RESTRICT_INSECURE); return; //don't drop the local client. It looks wrong. + } cl->drop = true; return; } @@ -3511,7 +3515,7 @@ static void QCBUILTIN PF_h2dprintf (progfuncs_t *prinst, struct globalvars_s *pr sprintf (temp, "%5.1f",v); Q_strncpyz(printable, PR_GetStringOfs(prinst, OFS_PARM0), sizeof(printable)); - while(pct = strstr(printable, "%s")) + while((pct = strstr(printable, "%s"))) { if ((pct-printable) + strlen(temp) + strlen(pct) > sizeof(printable)) break; @@ -3530,7 +3534,7 @@ static void QCBUILTIN PF_h2dprintv (progfuncs_t *prinst, struct globalvars_s *pr sprintf (temp, "'%5.1f %5.1f %5.1f'", G_VECTOR(OFS_PARM1)[0], G_VECTOR(OFS_PARM1)[1], G_VECTOR(OFS_PARM1)[2]); Q_strncpyz(printable, PR_GetStringOfs(prinst, OFS_PARM0), sizeof(printable)); - while(pct = strstr(printable, "%s")) + while((pct = strstr(printable, "%s"))) { if ((pct-printable) + strlen(temp) + strlen(pct) > sizeof(printable)) break; @@ -8900,15 +8904,15 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"rotatevectorsbytag", PF_Fixme, 0, 0, 0, 244, "vector(entity ent, float tagnum)"}, // #234 #ifdef SQL - {"sqlconnect", PF_sqlconnect, 0, 0, 0, 250, "float([string host], [string user], [string pass], [string defaultdb], [string driver]) sqlconnect (FTE_SQL) - {"sqldisconnect", PF_sqldisconnect, 0, 0, 0, 251, "void(float serveridx) sqldisconnect (FTE_SQL) - {"sqlopenquery", PF_sqlopenquery, 0, 0, 0, 252, "float(float serveridx, void(float serveridx, float queryidx, float rows, float columns, float eof) callback, float querytype, string query) sqlopenquery (FTE_SQL) - {"sqlclosequery", PF_sqlclosequery, 0, 0, 0, 253, "void(float serveridx, float queryidx) sqlclosequery (FTE_SQL) - {"sqlreadfield", PF_sqlreadfield, 0, 0, 0, 254, "string(float serveridx, float queryidx, float row, float column) sqlreadfield (FTE_SQL) - {"sqlerror", PF_sqlerror, 0, 0, 0, 255, "string(float serveridx, [float queryidx]) sqlerror (FTE_SQL) - {"sqlescape", PF_sqlescape, 0, 0, 0, 256, "string(float serveridx, string data) sqlescape (FTE_SQL) - {"sqlversion", PF_sqlversion, 0, 0, 0, 257, "string(float serveridx) sqlversion (FTE_SQL) - {"sqlreadfloat", PF_sqlreadfloat, 0, 0, 0, 258, "float(float serveridx, float queryidx, float row, float column) sqlreadfloat (FTE_SQL) + {"sqlconnect", PF_sqlconnect, 0, 0, 0, 250, "float(optional string host, optional string user, optional string pass, optional string defaultdb, optional string driver)"} // sqlconnect (FTE_SQL) + {"sqldisconnect", PF_sqldisconnect, 0, 0, 0, 251, "void(float serveridx)"} // sqldisconnect (FTE_SQL) + {"sqlopenquery", PF_sqlopenquery, 0, 0, 0, 252, "float(float serveridx, void(float serveridx, float queryidx, float rows, float columns, float eof) callback, float querytype, string query)"} // sqlopenquery (FTE_SQL) + {"sqlclosequery", PF_sqlclosequery, 0, 0, 0, 253, "void(float serveridx, float queryidx)"} // sqlclosequery (FTE_SQL) + {"sqlreadfield", PF_sqlreadfield, 0, 0, 0, 254, "string(float serveridx, float queryidx, float row, float column)"} // sqlreadfield (FTE_SQL) + {"sqlerror", PF_sqlerror, 0, 0, 0, 255, "string(float serveridx, optional float queryidx)"} // sqlerror (FTE_SQL) + {"sqlescape", PF_sqlescape, 0, 0, 0, 256, "string(float serveridx, string data)"} // sqlescape (FTE_SQL) + {"sqlversion", PF_sqlversion, 0, 0, 0, 257, "string(float serveridx)"} // sqlversion (FTE_SQL) + {"sqlreadfloat", PF_sqlreadfloat, 0, 0, 0, 258, "float(float serveridx, float queryidx, float row, float column)"} // sqlreadfloat (FTE_SQL) #endif {"stoi", PF_stoi, 0, 0, 0, 259, "int(string)"}, {"itos", PF_itos, 0, 0, 0, 260, "string(int)"}, @@ -9227,7 +9231,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs //DP_QC_WHICHPACK {"whichpack", PF_whichpack, 0, 0, 0, 503, "string(string filename)"},// //DP_CSQC_QUERYRENDERENTITY - {"getentity", PF_Fixme, 0, 0, 0, 504, "__variant(float entnum, fload fieldnum)"}, + {"getentity", PF_Fixme, 0, 0, 0, 504, "__variant(float entnum, float fieldnum)"}, //DP_QC_URI_ESCAPE {"uri_escape", PF_uri_escape, 0, 0, 0, 510, "string(string in)"},// @@ -9878,6 +9882,7 @@ void PR_DumpPlatform_f(void) {"FL_ONGROUND", "const float", QW|NQ|CS, FL_ONGROUND}, {"FL_PARTIALGROUND", "const float", QW|NQ|CS, FL_PARTIALGROUND}, {"FL_WATERJUMP", "const float", QW|NQ|CS, FL_WATERJUMP}, + {"FL_JUMPRELEASED", "const float", NQ|CS, FL_JUMPRELEASED}, {"FL_FINDABLE_NONSOLID","const float", QW|NQ|CS, FL_FINDABLE_NONSOLID}, // {"FL_MOVECHAIN_ANGLE", "const float", QW|NQ, FL_MOVECHAIN_ANGLE}, {"FL_LAGGEDMOVE", "const float", QW|NQ, FLQW_LAGGEDMOVE}, diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index c080c3328..456390784 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -23,12 +23,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef CLIENTONLY #define Q2EDICT_NUM(i) (q2edict_t*)((char *)ge->edicts+i*ge->edict_size) -#ifdef _WIN32 -#include "winquake.h" -#else -#include -#endif - void SV_Savegame_f (void); void SV_Loadgame_f (void); #define INVIS_CHAR1 12 @@ -163,7 +157,9 @@ cvar_t sv_port_tcp = CVARC("sv_port_tcp", "", SV_Tcpport_Callback); cvar_t sv_port_tcp6 = CVARC("sv_port_tcp6", "", SV_Tcpport6_Callback); #endif #endif +#ifdef HAVE_IPV4 cvar_t sv_port_ipv4 = CVARC("sv_port", "27500", SV_Port_Callback); +#endif #ifdef IPPROTO_IPV6 cvar_t sv_port_ipv6 = CVARC("sv_port_ipv6", "", SV_PortIPv6_Callback); #endif @@ -4117,8 +4113,10 @@ void SV_InitLocal (void) Cvar_Register (&sv_port_ipx, cvargroup_servercontrol); sv_port_ipx.restriction = RESTRICT_MAX; #endif +#ifdef HAVE_IPV4 Cvar_Register (&sv_port_ipv4, cvargroup_servercontrol); sv_port_ipv4.restriction = RESTRICT_MAX; +#endif Cvar_Register (&sv_reportheartbeats, cvargroup_servercontrol); @@ -4210,8 +4208,10 @@ void SV_InitLocal (void) int port = atoi(com_argv[p+1]); if (!port) port = PORT_QWSERVER; +#ifdef HAVE_IPV4 if (*sv_port_ipv4.string) Cvar_SetValue(&sv_port_ipv4, port); +#endif #ifdef IPPROTO_IPV6 if (*sv_port_ipv6.string) Cvar_SetValue(&sv_port_ipv6, port); diff --git a/engine/server/sv_mvd.c b/engine/server/sv_mvd.c index 26b202401..44edde2c0 100644 --- a/engine/server/sv_mvd.c +++ b/engine/server/sv_mvd.c @@ -82,8 +82,10 @@ void DestClose(mvddest_t *d, qboolean destroyfiles) BZ_Free(d->cache); if (d->file) VFS_CLOSE(d->file); +#ifdef HAVE_TCP if (d->socket) UDP_CloseSocket(d->socket); +#endif if (destroyfiles) { @@ -136,6 +138,9 @@ void DestFlush(qboolean compleate) break; case DEST_STREAM: +#ifndef HAVE_TCP + d->error = true; +#else if (d->cacheused && !d->error) { len = send(d->socket, d->cache, d->cacheused, 0); @@ -154,6 +159,7 @@ void DestFlush(qboolean compleate) d->error = true; } } +#endif break; case DEST_NONE: @@ -175,6 +181,10 @@ void DestFlush(qboolean compleate) void SV_MVD_RunPendingConnections(void) { +#ifndef HAVE_TCP + if (demo.pendingdest) + Sys_Error("demo.pendingdest not null"); +#else unsigned short ushort_result; char *e; int len; @@ -547,6 +557,7 @@ void SV_MVD_RunPendingConnections(void) } } } +#endif } int DestCloseAllFlush(qboolean destroyfiles, qboolean mvdonly) @@ -2062,6 +2073,9 @@ void SV_MVD_Record_f (void) void SV_MVD_QTVReverse_f (void) { +#ifndef HAVE_TCP + Con_Printf ("%s is not supported in this build\n", Cmd_Argv(0)); +#else char *ip; if (sv.state != ss_active) { @@ -2142,7 +2156,7 @@ void SV_MVD_QTVReverse_f (void) } //SV_MVD_Record (dest); - +#endif } /* @@ -2355,7 +2369,8 @@ void SV_MVDEasyRecord_f (void) SV_MVD_Record (SV_InitRecordFile(name2)); } -int MVD_StreamStartListening(int port) +#ifdef HAVE_TCP +static int MVD_StreamStartListening(int port) { int sock; @@ -2390,9 +2405,11 @@ int MVD_StreamStartListening(int port) return sock; } +#endif void SV_MVDStream_Poll(void) { +#ifdef HAVE_TCP static int listensocket=INVALID_SOCKET; static int listenport; int _true = true; @@ -2475,6 +2492,7 @@ void SV_MVDStream_Poll(void) SV_MVD_InitPendingStream(client, ip); // SV_MVD_Record (SV_InitStream(client)); +#endif } void SV_MVDList_f (void) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 08c901284..3c7effec4 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -1168,11 +1168,9 @@ SV_PreSpawn_f */ void SVQW_PreSpawn_f (void) { - unsigned buf, bufs; + unsigned initbuf, buf, start; unsigned check; - unsigned statics; - if (host_client->state != cs_connected) { Con_Printf ("prespawn not valid -- already spawned\n"); @@ -1187,22 +1185,7 @@ void SVQW_PreSpawn_f (void) return; } -#ifdef SERVER_DEMO_PLAYBACK - if (sv.democausesreconnect) - bufs = sv.num_demosignon_buffers; - else -#endif - bufs = sv.num_signon_buffers; - statics = sv.num_static_entities; - buf = atoi(Cmd_Argv(2)); - - if (buf >= bufs+statics+sv.world.num_edicts+255) - { - SV_EndRedirect(); - Con_Printf ("SV_Modellist_f: %s send an invalid index\n", host_client->name); - SV_DropClient(host_client); - return; - } + buf = initbuf = atoi(Cmd_Argv(2)); if (!buf) { @@ -1243,186 +1226,210 @@ void SVQW_PreSpawn_f (void) return; } - if (buf >= bufs #ifdef SERVER_DEMO_PLAYBACK - && !sv.democausesreconnect + if (sv.democausesreconnect) + { + if (host_client->netchan.message.cursize+sv.demosignon_buffer_size[buf]+30 < host_client->netchan.message.maxsize) + { + SZ_Write (&host_client->netchan.message, + sv.demosignon_buffers[buf], + sv.demosignon_buffer_size[buf]); + buf++; + } + start = sv.num_demosignon_buffers; + } + else #endif - ) { int i; entity_state_t from; entity_state_t *state; edict_t *ent; svcustomtents_t *ctent; + start = 0; - - memset(&from, 0, sizeof(from)); - while (host_client->netchan.message.cursize < (host_client->netchan.message.maxsize/2)) //static entities + if (buf >= start) { - if (buf - bufs >= sv.num_static_entities) - break; - - state = &sv_staticentities[buf - bufs]; - buf++; - - if (host_client->fteprotocolextensions & PEXT_SPAWNSTATIC2) + while (host_client->netchan.message.cursize < (host_client->netchan.message.maxsize/2)) //static entities { - /*if it uses some new feature, use the updated spawnstatic*/ - if (state->hexen2flags || state->trans || state->modelindex >= 256 || state->frame > 255 || state->scale || state->abslight) + i = buf - start; + if (i >= sv.num_signon_buffers) + break; + + if (host_client->netchan.message.cursize+sv.signon_buffer_size[i]+30 < host_client->netchan.message.maxsize) { - MSG_WriteByte(&host_client->netchan.message, svc_spawnstatic2); - SVQW_WriteDelta(&from, state, &host_client->netchan.message, true, host_client->fteprotocolextensions); + SZ_Write (&host_client->netchan.message, + sv.signon_buffers[i], + sv.signon_buffer_size[i]); + buf++; + } + else + break; + } + } + start += sv.num_signon_buffers; + + if (buf >= start) + { + memset(&from, 0, sizeof(from)); + while (host_client->netchan.message.cursize < (host_client->netchan.message.maxsize/2)) //static entities + { + if (buf - start >= sv.num_static_entities) + break; + + state = &sv_staticentities[buf - start]; + buf++; + + if (host_client->fteprotocolextensions & PEXT_SPAWNSTATIC2) + { + /*if it uses some new feature, use the updated spawnstatic*/ + if (state->hexen2flags || state->trans || state->modelindex >= 256 || state->frame > 255 || state->scale || state->abslight) + { + MSG_WriteByte(&host_client->netchan.message, svc_spawnstatic2); + SVQW_WriteDelta(&from, state, &host_client->netchan.message, true, host_client->fteprotocolextensions); + continue; + } + } + /*couldn't use protocol extensions? + use the fallback, unless the model is invalid as that's silly*/ + if (state->modelindex < 256) + { + MSG_WriteByte(&host_client->netchan.message, svc_spawnstatic); + + MSG_WriteByte (&host_client->netchan.message, state->modelindex); + + MSG_WriteByte (&host_client->netchan.message, state->frame); + MSG_WriteByte (&host_client->netchan.message, (int)state->colormap); + MSG_WriteByte (&host_client->netchan.message, (int)state->skinnum); + for (i=0 ; i<3 ; i++) + { + MSG_WriteCoord(&host_client->netchan.message, state->origin[i]); + MSG_WriteAngle(&host_client->netchan.message, state->angles[i]); + } continue; } } - /*couldn't use protocol extensions? - use the fallback, unless the model is invalid as that's silly*/ - if (state->modelindex < 256) - { - MSG_WriteByte(&host_client->netchan.message, svc_spawnstatic); - - MSG_WriteByte (&host_client->netchan.message, state->modelindex); - - MSG_WriteByte (&host_client->netchan.message, state->frame); - MSG_WriteByte (&host_client->netchan.message, (int)state->colormap); - MSG_WriteByte (&host_client->netchan.message, (int)state->skinnum); - for (i=0 ; i<3 ; i++) - { - MSG_WriteCoord(&host_client->netchan.message, state->origin[i]); - MSG_WriteAngle(&host_client->netchan.message, state->angles[i]); - } - continue; - } } - while (host_client->netchan.message.cursize < (host_client->netchan.message.maxsize/2)) //baselines + start += sv.num_static_entities; + + if (buf >= start) { - if (buf - bufs - sv.num_static_entities >= sv.world.num_edicts) - break; + while (host_client->netchan.message.cursize < (host_client->netchan.message.maxsize/2)) + { + i = buf - start; + if (i >= 255) + break; - ent = EDICT_NUM(svprogfuncs, buf - bufs - sv.num_static_entities); + ctent = &sv.customtents[i]; - state = &ent->baseline; - if (!state->number || !state->modelindex) - { //ent doesn't have a baseline buf++; - continue; - } - - if (!ent) - { - MSG_WriteByte(&host_client->netchan.message, svc_spawnbaseline); - - MSG_WriteShort (&host_client->netchan.message, buf - bufs - sv.num_static_entities); - - MSG_WriteByte (&host_client->netchan.message, 0); - - MSG_WriteByte (&host_client->netchan.message, 0); - MSG_WriteByte (&host_client->netchan.message, 0); - MSG_WriteByte (&host_client->netchan.message, 0); - for (i=0 ; i<3 ; i++) - { - MSG_WriteCoord(&host_client->netchan.message, 0); - MSG_WriteAngle(&host_client->netchan.message, 0); + if (!*ctent->particleeffecttype) + { //effect isn't registered. + continue; } - } - else if (state->number >= host_client->max_net_ents || state->modelindex >= host_client->maxmodels) - { - /*can't send this ent*/ - } - else if (host_client->fteprotocolextensions & PEXT_SPAWNSTATIC2) - { - MSG_WriteByte(&host_client->netchan.message, svcfte_spawnbaseline2); - SVQW_WriteDelta(&from, state, &host_client->netchan.message, true, host_client->fteprotocolextensions); - } - else if (state->modelindex < 256) - { - MSG_WriteByte(&host_client->netchan.message, svc_spawnbaseline); - MSG_WriteShort (&host_client->netchan.message, buf - bufs - sv.num_static_entities); - - MSG_WriteByte (&host_client->netchan.message, state->modelindex); - - MSG_WriteByte (&host_client->netchan.message, state->frame); - MSG_WriteByte (&host_client->netchan.message, (int)state->colormap); - MSG_WriteByte (&host_client->netchan.message, (int)state->skinnum); - for (i=0 ; i<3 ; i++) + if (host_client->fteprotocolextensions & PEXT_CUSTOMTEMPEFFECTS) { - MSG_WriteCoord(&host_client->netchan.message, state->origin[i]); - MSG_WriteAngle(&host_client->netchan.message, state->angles[i]); - } - } - - buf++; - } - while (host_client->netchan.message.cursize < (host_client->netchan.message.maxsize/2)) - { - i = buf - bufs - sv.num_static_entities - sv.world.num_edicts; - if (i >= 255) - break; - - ctent = &sv.customtents[i]; - - buf++; - if (!*ctent->particleeffecttype) - { //effect isn't registered. - continue; - } - - if (host_client->fteprotocolextensions & PEXT_CUSTOMTEMPEFFECTS) - { - MSG_WriteByte(&host_client->netchan.message, svcfte_customtempent); - MSG_WriteByte(&host_client->netchan.message, 255); - MSG_WriteByte(&host_client->netchan.message, i); - MSG_WriteByte(&host_client->netchan.message, ctent->netstyle); - MSG_WriteString(&host_client->netchan.message, ctent->particleeffecttype); - if (ctent->netstyle & CTE_STAINS) - { - MSG_WriteChar(&host_client->netchan.message, ctent->stain[0]); - MSG_WriteChar(&host_client->netchan.message, ctent->stain[0]); - MSG_WriteChar(&host_client->netchan.message, ctent->stain[0]); - MSG_WriteByte(&host_client->netchan.message, ctent->radius); - } - if (ctent->netstyle & CTE_GLOWS) - { - MSG_WriteByte(&host_client->netchan.message, ctent->dlightrgb[0]); - MSG_WriteByte(&host_client->netchan.message, ctent->dlightrgb[1]); - MSG_WriteByte(&host_client->netchan.message, ctent->dlightrgb[2]); - MSG_WriteByte(&host_client->netchan.message, ctent->dlightradius); - MSG_WriteByte(&host_client->netchan.message, ctent->dlighttime); + MSG_WriteByte(&host_client->netchan.message, svcfte_customtempent); + MSG_WriteByte(&host_client->netchan.message, 255); + MSG_WriteByte(&host_client->netchan.message, i); + MSG_WriteByte(&host_client->netchan.message, ctent->netstyle); + MSG_WriteString(&host_client->netchan.message, ctent->particleeffecttype); + if (ctent->netstyle & CTE_STAINS) + { + MSG_WriteChar(&host_client->netchan.message, ctent->stain[0]); + MSG_WriteChar(&host_client->netchan.message, ctent->stain[0]); + MSG_WriteChar(&host_client->netchan.message, ctent->stain[0]); + MSG_WriteByte(&host_client->netchan.message, ctent->radius); + } + if (ctent->netstyle & CTE_GLOWS) + { + MSG_WriteByte(&host_client->netchan.message, ctent->dlightrgb[0]); + MSG_WriteByte(&host_client->netchan.message, ctent->dlightrgb[1]); + MSG_WriteByte(&host_client->netchan.message, ctent->dlightrgb[2]); + MSG_WriteByte(&host_client->netchan.message, ctent->dlightradius); + MSG_WriteByte(&host_client->netchan.message, ctent->dlighttime); + } } } } - } - else if (buf >= bufs) - { - buf = bufs+sv.num_static_entities+sv.world.num_edicts+255; - } - else - { -#ifdef SERVER_DEMO_PLAYBACK - if (sv.democausesreconnect) + start += 255; + + if (buf >= start) { - if (host_client->netchan.message.cursize+sv.signon_buffer_size[buf]+30 < host_client->netchan.message.maxsize) + while (host_client->netchan.message.cursize < (host_client->netchan.message.maxsize/2)) //baselines { - SZ_Write (&host_client->netchan.message, - sv.demosignon_buffers[buf], - sv.demosignon_buffer_size[buf]); - buf++; - } - } - else -#endif - { - if (host_client->netchan.message.cursize+sv.signon_buffer_size[buf]+30 < host_client->netchan.message.maxsize) - { - SZ_Write (&host_client->netchan.message, - sv.signon_buffers[buf], - sv.signon_buffer_size[buf]); + if (buf - start >= sv.world.num_edicts) + break; + + ent = EDICT_NUM(svprogfuncs, buf - start); + + state = &ent->baseline; + if (!state->number || !state->modelindex) + { //ent doesn't have a baseline buf++; + continue; + } + + if (!ent) + { + MSG_WriteByte(&host_client->netchan.message, svc_spawnbaseline); + + MSG_WriteShort (&host_client->netchan.message, buf - start); + + MSG_WriteByte (&host_client->netchan.message, 0); + + MSG_WriteByte (&host_client->netchan.message, 0); + MSG_WriteByte (&host_client->netchan.message, 0); + MSG_WriteByte (&host_client->netchan.message, 0); + for (i=0 ; i<3 ; i++) + { + MSG_WriteCoord(&host_client->netchan.message, 0); + MSG_WriteAngle(&host_client->netchan.message, 0); + } + } + else if (state->number >= host_client->max_net_ents || state->modelindex >= host_client->maxmodels) + { + /*can't send this ent*/ + } + else if (host_client->fteprotocolextensions & PEXT_SPAWNSTATIC2) + { + MSG_WriteByte(&host_client->netchan.message, svcfte_spawnbaseline2); + SVQW_WriteDelta(&from, state, &host_client->netchan.message, true, host_client->fteprotocolextensions); + } + else if (state->modelindex < 256) + { + MSG_WriteByte(&host_client->netchan.message, svc_spawnbaseline); + + MSG_WriteShort (&host_client->netchan.message, buf - start); + + MSG_WriteByte (&host_client->netchan.message, state->modelindex); + + MSG_WriteByte (&host_client->netchan.message, state->frame); + MSG_WriteByte (&host_client->netchan.message, (int)state->colormap); + MSG_WriteByte (&host_client->netchan.message, (int)state->skinnum); + for (i=0 ; i<3 ; i++) + { + MSG_WriteCoord(&host_client->netchan.message, state->origin[i]); + MSG_WriteAngle(&host_client->netchan.message, state->angles[i]); + } + } + + buf++; } } + start += sv.world.num_edicts; } - if (buf == bufs+sv.num_static_entities+sv.world.num_edicts+255) + + if (initbuf >= start) + { + SV_EndRedirect(); + Con_Printf ("SV_Modellist_f: %s send an invalid index\n", host_client->name); + SV_DropClient(host_client); + return; + } + + if (buf == start) { // all done prespawning MSG_WriteByte (&host_client->netchan.message, svc_stufftext); MSG_WriteString (&host_client->netchan.message, va("cmd spawn %i\n",svs.spawncount) ); @@ -4584,6 +4591,20 @@ void SVNQ_PreSpawn_f (void) } st = 0; + + if (buf >= st) + { + while (host_client->netchan.message.cursize < (host_client->netchan.message.maxsize/2)) + { + i = buf-st; + if (i >= sv.num_signon_buffers) + break; + buf++; + SZ_Write (&host_client->netchan.message, sv.signon_buffers[i], sv.signon_buffer_size[i]); + } + } + st += sv.num_signon_buffers; + if (buf >= st) { while (host_client->netchan.message.cursize < (host_client->netchan.message.maxsize/2)) //baselines @@ -4655,19 +4676,6 @@ void SVNQ_PreSpawn_f (void) } st += sv.world.max_edicts; - if (buf >= st) - { - while (host_client->netchan.message.cursize < (host_client->netchan.message.maxsize/2)) - { - i = buf-st; - if (i >= sv.num_signon_buffers) - break; - buf++; - SZ_Write (&host_client->netchan.message, sv.signon_buffers[i], sv.signon_buffer_size[i]); - } - } - st += sv.num_signon_buffers; - if (st == buf) { MSG_WriteByte (&host_client->netchan.message, svc_signonnum); diff --git a/engine/server/svmodel.c b/engine/server/svmodel.c index 7620a1aeb..013d42884 100644 --- a/engine/server/svmodel.c +++ b/engine/server/svmodel.c @@ -25,6 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. model_t *loadmodel; char loadname[32]; // for hunk tags +qboolean GL_LoadHeightmapModel (model_t *mod, void *buffer); qboolean Mod_LoadBrushModel (model_t *mod, void *buffer); qboolean Mod_LoadQ2BrushModel (model_t *mod, void *buffer); qboolean D3_LoadMap_CollisionMap(model_t *mod, char *buf); @@ -479,6 +480,14 @@ model_t *Mod_LoadModel (model_t *mod, qboolean crash) break; } #endif +#ifdef TERRAIN + if (!strcmp(com_token, "terrain")) //custom format, text based. + { + if (!GL_LoadHeightmapModel(mod, buf)) + goto couldntload; + break; + } +#endif Con_Printf (CON_ERROR "Mod_NumForName: %s: format not recognised\n", mod->name); couldntload: