From f72f98c46e8895fbc6ac488cd0eb13195f230f40 Mon Sep 17 00:00:00 2001 From: Chris Dawalt Date: Tue, 28 Sep 2021 06:51:21 -0400 Subject: [PATCH] Simple and extravagant prediction demos for useItems --- src/client/cmds.qc | 22 +-- src/client/game_event.qc | 7 + src/client/progs.src | 8 +- src/client/view.qc | 13 ++ src/server/progs.src | 13 +- src/shared/defs.h | 20 +++ src/shared/event_custom.qc | 19 ++- src/shared/event_enum.h | 3 + src/shared/input.qc | 299 ++++++++++++++++++++++++++++++++++--- src/shared/player.h | 4 + src/shared/player.qc | 25 +++- 11 files changed, 373 insertions(+), 60 deletions(-) diff --git a/src/client/cmds.qc b/src/client/cmds.qc index 0068e0a..b0a3d24 100644 --- a/src/client/cmds.qc +++ b/src/client/cmds.qc @@ -42,32 +42,16 @@ ClientGame_ConsoleCommand(void) //pSeatLocal->m_bFireModeFreshPress = TRUE; break; -#if USE_ITEMS_PREDICTION_TEST == 0 //case "useitems" // TS_playerUseItems(); // break; case "+useitems": - pl = (player)pSeat->m_ePlayer; - if( !(pl.gflags & GF_UNUSED3)){ - TS_playerUseItems(); - } - pl.gflags |= GF_UNUSED3; + Input_useItems_press(); break; case "-useitems": - pl = (player)pSeat->m_ePlayer; - pl.gflags &= ~GF_UNUSED3; + Input_useItems_release(); break; -#else - case "+useitems": - //TS_playerUseItems(); - //pSeatLocal->m_bUseItemsFreshPress = TRUE; - pSeatLocal->m_bUseItems = TRUE; - break; - case "-useitems": - pSeatLocal->m_bUseItems = FALSE; - break; -#endif - + case "usepowerup": TS_playerUsePowerup(); break; diff --git a/src/client/game_event.qc b/src/client/game_event.qc index e9173ab..2ce8b38 100644 --- a/src/client/game_event.qc +++ b/src/client/game_event.qc @@ -206,6 +206,13 @@ ClientGame_EventParse(float fHeader) case EVENT_TS::USEITEMS_CHANGE_CALLBACK:{ pl.clientUseItemsCallback(); }break; + +#if OTHER_PREDICTION_TEST == 1 + case EVENT_TS::CUSTOM_PREDICTION_CALLBACK:{ + Custom_Prediction_Server_Callback(pl); + }break; +#endif + case EVENT_TS::TEST:{ //printfline("EVENT_TS::TEST HAPPENED"); //clearscene(); diff --git a/src/client/progs.src b/src/client/progs.src index 5697064..c8f61fe 100644 --- a/src/client/progs.src +++ b/src/client/progs.src @@ -1,11 +1,7 @@ // SHARED CONFIG SETTING - set this consistently between client/server progs! -// If 0, nothing special. -// If 1, Makes some changes to enable prediction for useitems. -// Spams the turn-on click noise though. -// Doubt this is worth further support as I don't know how to support this for -// more than one extra feature, the INPUT_BUTTON# list is exhausted after this. -#define USE_ITEMS_PREDICTION_TEST 0 +// See server copy for notes +#define OTHER_PREDICTION_TEST 2 /////////////////////////////////////////////////////////////////////////////// diff --git a/src/client/view.qc b/src/client/view.qc index d086b6b..2f2cd9e 100644 --- a/src/client/view.qc +++ b/src/client/view.qc @@ -365,6 +365,8 @@ TS_View_DrawSpecialEffects_Weapon( vector* recentLaserHitPosVar ) { + + const float drawAlpha = 1.0; const vector lasColor = [1.0, 0, 0]; @@ -473,6 +475,17 @@ TS_View_DrawSpecialEffects_Weapon( void TS_View_DrawExtraEffects(player pl, int thirdperson) { + + + if(pl.inventoryEquippedIndex > -1){ + weapondynamic_t dynaRef2 = pl.ary_myWeapons[pl.inventoryEquippedIndex]; + //printfline("---TS_View_DrawSpecialEffects_Weapon--- %d", (float)((dynaRef2.iBitsUpgrade_on & BITS_WEAPONOPT_FLASHLIGHT) != 0)); + + //dynaRef2.iBitsUpgrade_on = 0; + } + + + //int thirdperson = (autocvar_cl_thirdperson == TRUE || this.entnum != player_localentnum); //base_player pp = (base_player)this; diff --git a/src/server/progs.src b/src/server/progs.src index f746993..e27754c 100644 --- a/src/server/progs.src +++ b/src/server/progs.src @@ -2,10 +2,15 @@ // SHARED CONFIG SETTING - set this consistently between client/server progs! // If 0, nothing special. // If 1, Makes some changes to enable prediction for useitems. -// Spams the turn-on click noise though. -// Doubt this is worth further support as I don't know how to support this for -// more than one extra feature, the INPUT_BUTTON# list is exhausted after this. -#define USE_ITEMS_PREDICTION_TEST 0 +// See all mentions of this throughout the codebase for the places it changes. +// Mainly it adds a chunk that immitates FTE prediction at the end of +// ts/src/shared/input.qc, as a demonstration more than anything. This is likely +// overkill. This also stops sending the weapon's "iBitsUpgrade_on" in normal +// player.qc script and does so along custom inputframe updates in input.qc. +// If 2, use "input_impulse" instead. Well gee, look at that. +// Also comes with a separate check for playing the click noise as the +// 'input_sequence==clientcommandframe' check does not work like the custom version. +#define OTHER_PREDICTION_TEST 2 /////////////////////////////////////////////////////////////////////////////// diff --git a/src/shared/defs.h b/src/shared/defs.h index ee6ca05..de8d28e 100644 --- a/src/shared/defs.h +++ b/src/shared/defs.h @@ -100,3 +100,23 @@ void Weapons_Draw(void); void Weapons_Holster(void); + +#if OTHER_PREDICTION_TEST == 1 +#ifdef CLIENT +// test +void Custom_Prediction_Server_Callback(player pl); +void Custom_Predict_EntityUpdate(player pl); +void Custom_Predict_PlayerPreFrame(player pl); +void Custom_Predict_PlayerPostFrame(player pl); +var float custom_input_sequence = 0; +var float custom_clientcommandframe = 0; +var float custom_servercommandframe = 0; +#else +var float custom_servercommandframe = 0; +void Custom_EvaluateEntity(player pl); +#endif + +#elif OTHER_PREDICTION_TEST == 2 +//var BOOL isFreshInput = FALSE; +#endif//OTHER_PREDICTION_TEST + diff --git a/src/shared/event_custom.qc b/src/shared/event_custom.qc index f1725d3..8ac9877 100644 --- a/src/shared/event_custom.qc +++ b/src/shared/event_custom.qc @@ -623,8 +623,7 @@ TS_playerUseItems(void){ return; } - -#if USE_ITEMS_PREDICTION_TEST == 0 +#if OTHER_PREDICTION_TEST == 0 #ifdef CLIENT sendevent("TS_playerUseItems", ""); @@ -639,8 +638,6 @@ TS_playerUseItems(void){ // always do this then _TS_playerUseItems(); #endif - - } @@ -723,7 +720,21 @@ _TS_playerUseItems(void){ // Let's be a clientside sound only. #ifdef CLIENT + + +#if OTHER_PREDICTION_TEST == 1 +// enforce a check that this is the first inputframe, not repeats, to avoid sound spam +if(custom_input_sequence==custom_clientcommandframe){ +#elif OTHER_PREDICTION_TEST == 2 +//if(input_sequence==clientcommandframe){ +//if(isFreshInput){ +if(0){ // never do it here, elsewhere instead +#else +// nothing special +if(1){ +#endif localsound("weapons/switch.wav", CHAN_AUTO, 1.0f); +} #endif diff --git a/src/shared/event_enum.h b/src/shared/event_enum.h index 7d482c3..67a3fd2 100644 --- a/src/shared/event_enum.h +++ b/src/shared/event_enum.h @@ -15,6 +15,9 @@ enum EVENT_TS{ SOUNDPITCHED, SOUNDPITCHED_CHANNEL, USEITEMS_CHANGE_CALLBACK, + + CUSTOM_PREDICTION_CALLBACK, + TEST, }; diff --git a/src/shared/input.qc b/src/shared/input.qc index 287c4d6..fd4b4e5 100644 --- a/src/shared/input.qc +++ b/src/shared/input.qc @@ -15,13 +15,32 @@ */ + void processInputs(void); #ifdef CLIENT void PreSpawn_Input(void); +// externs +void HUD_DrawWeaponSelect_Trigger(void); +BOOL TS_HUD_CloseWeaponSelect(BOOL); #endif + + + + + +#if OTHER_PREDICTION_TEST == 1 +float custom_input_buttons; +float ary_custom_input_buttons[512]; +BOOL ary_custom_input_sentYet[512]; + +void Custom_Game_Input(void); +#endif + + + // WARNING! This is only called by PMove_Run, which is only called // for spawned players. This is not called for players in spectator, // so if script to check for clicking while in spectator with no menu @@ -333,27 +352,15 @@ Game_Input(void) } */ - - -#if USE_ITEMS_PREDICTION_TEST == 1 - //INPUT_BUTTON2 reserved for jump... - //INPUT_BUTTON6 - //INPUT_BUTTON7 (used for sneaking currently) - if(input_buttons & INPUT_BUTTON6){ - //printfline("IM here what"); - - if( !(pl.gflags & GF_UNUSED3)){ - TS_playerUseItems(); - } - pl.gflags |= GF_UNUSED3; - }else{ - pl.gflags &= ~GF_UNUSED3; - +#if OTHER_PREDICTION_TEST == 2 + if(input_impulse == 115){ + TS_playerUseItems(); } + // great. So that won't even work. + //isFreshInput = FALSE; #endif - }//Game_Input @@ -415,6 +422,8 @@ void processInputs(void){ + + #ifdef CLIENT // called when not ingame for sending a spawn request. @@ -455,8 +464,55 @@ void PreSpawn_Input(void){ -void HUD_DrawWeaponSelect_Trigger(void); -BOOL TS_HUD_CloseWeaponSelect(BOOL); + +#ifdef CLIENT + +// redirects from cmd.qc to be less of a mess over there. +void Input_useItems_press(void){ + player pl = (player)pSeat->m_ePlayer; + +#if OTHER_PREDICTION_TEST == 0 + if( !(pl.gflags & GF_UNUSED3)){ + TS_playerUseItems(); + } + pl.gflags |= GF_UNUSED3; +#elif OTHER_PREDICTION_TEST == 1 + pSeatLocal->m_bUseItems = TRUE; +#elif OTHER_PREDICTION_TEST == 2 + pSeatLocal->m_bUseItems = TRUE; + //TAGGG - play the click only this once + if( !(pl.gflags & GF_UNUSED3)){ + localsound("weapons/switch.wav", CHAN_AUTO, 1.0f); + } + pl.gflags |= GF_UNUSED3; +#endif +}//Input_useItems_press + + +void Input_useItems_release(void){ + player pl = (player)pSeat->m_ePlayer; + +#if OTHER_PREDICTION_TEST == 0 + pl.gflags &= ~GF_UNUSED3; +#elif OTHER_PREDICTION_TEST == 1 + pSeatLocal->m_bUseItems = FALSE; +#elif OTHER_PREDICTION_TEST == 2 + pSeatLocal->m_bUseItems = FALSE; + pl.gflags &= ~GF_UNUSED3; +#endif +}//Input_useItems_release + +#endif + + + + + + + + + + // Not yet called by Nuclide! A series of notes for now // (move to clientside files then) @@ -479,9 +535,11 @@ void ClientGame_Input_Frame(void){ // affecting other inputs like crouch is unnecessary, that causes the player to // uncrouch if changing weapons while crouched. - player pl = (player)pSeat->m_ePlayer; + if(pl == NULL)return; // ??? + + if(pl.iState != PLAYER_STATE::SPAWNED && pSeatLocal->m_flUI_Display != UI_SCREEN::NONE){ // If buying, don't allow any movement inputs. // The above is a similar check as "g_vguiWidgetCount > 0", but the buymenu does not @@ -512,23 +570,216 @@ void ClientGame_Input_Frame(void){ if (pSeatLocal->m_iInputSpeed == TRUE) { input_buttons |= INPUT_BUTTON7; //self.flags |= FL_SNEAK; + }else{ //self.flags &= ~FL_SNEAK; } //printfline("input_buttons: %d", (INPUT_BUTTON7 & input_buttons) ); -#if USE_ITEMS_PREDICTION_TEST == 1 + // NOTE - GF_UNUSED3 is being used for playing the toggle-switch sound in + // +#if OTHER_PREDICTION_TEST == 2 + // using GF_UNUSED4 instead of GF_UNUSED3 because that one is + // being used to play the click-noise on toggling instead. + // Yes, really. if(pSeatLocal->m_bUseItems){ - //pSeatLocal->m_bUseItems = FALSE; - input_buttons |= INPUT_BUTTON6; + if( !(pl.gflags & GF_UNUSED4)){ + input_impulse = 115; + //isFreshInput = TRUE; + } + pl.gflags |= GF_UNUSED4; + }else{ + pl.gflags &= ~GF_UNUSED4; } #endif +}// ClientGame_InputFrame + + +#endif // CLIENT + + + + + + + + +//TAGGG - most of the alternate prediction demo. +// Replicates what's seen in Nuclide's src/predict.qc to make +// the toggle-options feature predicted without involving FTE's +// input_buttons. +////////////////////////////////////////////////////////////////////////////////// +#if OTHER_PREDICTION_TEST == 1 + +// Shared +void Custom_Game_Input(void){ + player pl = (player)self; + if(custom_input_buttons == TRUE){ + if( !(pl.gflags & GF_UNUSED3)){ + //printfline("---CLICK---"); + TS_playerUseItems(); + } + pl.gflags |= GF_UNUSED3; + }else{ + pl.gflags &= ~GF_UNUSED3; + + } +}//Custom_Game_Input + + + +#ifdef CLIENT + +void Custom_ClientGame_Input_Frame(void){ + + if(pSeatLocal->m_bUseItems){ + //pSeatLocal->m_bUseItems = FALSE; + //input_buttons |= INPUT_BUTTON6; + ary_custom_input_buttons[custom_clientcommandframe % 512] = TRUE; + } + + if(!ary_custom_input_sentYet[custom_clientcommandframe % 512]){ + sendevent("GSVRCC", "ff", custom_clientcommandframe, ary_custom_input_buttons[custom_clientcommandframe % 512]); + ary_custom_input_sentYet[custom_clientcommandframe % 512] = TRUE; + } + //printfline("CURRENT CLIENTCOMMAND FRAME: %d", custom_clientcommandframe); + + // And refresh the entry in the queue somewhere before this point, so that + // loop-arounds don't cause issues from running into leftover/out-of-date + // memory being set but not being worth paying attention to. + float subtr = custom_clientcommandframe - 256; + if(subtr < 0){subtr += 512;} + float modulo = subtr % 512; + ary_custom_input_buttons[modulo] = 0; + ary_custom_input_sentYet[modulo] = FALSE; +} + + +void Custom_Predict_EntityUpdate(player pl){ + + if (pl.entnum == player_localentnum) { + /* run the player physics from the last approved servercommandframe to the current one */ + for (int i = pl.custom_sequence+1; i <= custom_servercommandframe; i++) { + /* ...maybe the input state is too old? */ + //if (!getinputstate(i)) { + // break; + //} + custom_input_sequence = i; + //pl.Physics_Run(); + + custom_input_buttons = ary_custom_input_buttons[custom_input_sequence % 512]; + Custom_Game_Input(); + } + } + + pl.custom_sequence = custom_servercommandframe; +} + +void Custom_Predict_PlayerPreFrame(player pl){ + + //printfline("---PREFRAME---"); + + for (int i = pl.custom_sequence + 1; i <= custom_clientcommandframe; i++) { + + //float flSuccess = getinputstate(i); + //if (flSuccess == FALSE) { + // continue; + //} + + if (i==custom_clientcommandframe){ + //CSQC_Input_Frame(); + Custom_ClientGame_Input_Frame(); + } + + /* don't do partial frames, aka incomplete input packets */ + //if (input_timelength == 0) { + // break; + //} + + /* this global is for our shared random number seed */ + custom_input_sequence = i; + + /* run our custom physics */ + //pl.Physics_Run(); + + custom_input_buttons = ary_custom_input_buttons[custom_input_sequence % 512]; + Custom_Game_Input(); + } + custom_clientcommandframe++; +} + +void Custom_Predict_PlayerPostFrame(player pl){ + //printfline("---ROLLBACK---"); +} + + +// called by server +void Custom_Prediction_Server_Callback(player pl){ + + float received_servercommandframe = readfloat(); + custom_servercommandframe = received_servercommandframe; + + if(pl != NULL){ + Custom_Predict_EntityUpdate(pl); + } + + if(pl != NULL){ + int EXTRA = readint(); + if(EXTRA > -1){ + pl.ary_myWeapons[pl.inventoryEquippedIndex].iBitsUpgrade_on = EXTRA; + //pl.ary_myWeapons[pl.inventoryEquippedIndex].iBitsUpgrade_on_net = pl.ary_myWeapons[pl.inventoryEquippedIndex].iBitsUpgrade_on; + } + } } -#endif + +#else + + // SERVER + +// short for GameServer_RunClientCommand. +void +CSEv_GSVRCC_ff(float arg_scf, float arg_ibc){ + //TS_playerUseItems(); + + custom_servercommandframe = arg_scf; + custom_input_buttons = arg_ibc; + Custom_Game_Input(); + + // send a message back! + player pl = (player)self; + WriteByte( MSG_MULTICAST, SVC_CGAMEPACKET ); + WriteByte( MSG_MULTICAST, EVENT_TS::CUSTOM_PREDICTION_CALLBACK ); + WriteFloat( MSG_MULTICAST, custom_servercommandframe ); + + if(pl.inventoryEquippedIndex > -1){ + WriteInt( MSG_MULTICAST, pl.ary_myWeapons[pl.inventoryEquippedIndex].iBitsUpgrade_on ); + }else{ + WriteInt( MSG_MULTICAST, -1 ); + } + + msg_entity = pl; + multicast( [0,0,0], MULTICAST_ONE ); + +} + +void Custom_EvaluateEntity(player pl){ + + // SAVE_STATE + //if(pl.inventoryEquippedIndex > -1){ + // pl.ary_myWeapons[pl.inventoryEquippedIndex].iBitsUpgrade_on_net = pl.ary_myWeapons[pl.inventoryEquippedIndex].iBitsUpgrade_on; + //} + +} + +#endif// CLIENT vs. SERVER + + +#endif// OTHER_PREDICTION_TEST +////////////////////////////////////////////////////////////////////////////////// diff --git a/src/shared/player.h b/src/shared/player.h index d2b696e..f98d835 100644 --- a/src/shared/player.h +++ b/src/shared/player.h @@ -44,6 +44,10 @@ enum PLAYER_STATE{ class player:base_player { +#ifdef CLIENT + int custom_sequence; +#endif + // On death, the player cannot change the camera to fake-specator until at least 1 second // has passed. // On death, set this to 2.5. If it is less than 1.5, diff --git a/src/shared/player.qc b/src/shared/player.qc index 16e3569..f2a430f 100644 --- a/src/shared/player.qc +++ b/src/shared/player.qc @@ -69,6 +69,8 @@ player::ReceiveEntity(float new, float fl) { /* the generic client attributes */ base_player::ReceiveEntity(new, fl); + + /* animation */ if (fl & PLAYER_TOPFRAME) { @@ -324,7 +326,9 @@ void player::ReceiveEntity_ary_myWeapons(int i){ //ary_myWeapons[i].iSlots = readbyte(); ary_myWeapons[i].iClipLeft = readbyte(); ary_myWeapons[i].iClipAkimboLeft = readbyte(); +#if OTHER_PREDICTION_TEST != 1 ary_myWeapons[i].iBitsUpgrade_on = readbyte(); +#endif int newFir = readbyte(); @@ -471,6 +475,12 @@ player::PredictPreFrame(void) // reach the Nuclide logic looking at it in time to be applied. // No need here now I think? //this.viewzoom = this.flZoomCurrent; + + +#if OTHER_PREDICTION_TEST == 1 + Custom_Predict_PlayerPreFrame(this); +#endif + } /* @@ -485,7 +495,6 @@ player::PredictPostFrame(void) { //printfline("---PREDICT POST FRAME"); - /* #ifdef CLIENT if(iState == PLAYER_STATE::SPAWNED){ @@ -603,8 +612,13 @@ player::PredictPostFrame(void) ROLL_BACK_ARY(ary_ammoTotal, i); } +#if OTHER_PREDICTION_TEST == 1 + Custom_Predict_PlayerPostFrame(this); +#endif + } + #else void player::EvaluateEntity(void) @@ -830,6 +844,11 @@ player::EvaluateEntity(void) } } + +#if OTHER_PREDICTION_TEST == 1 + Custom_EvaluateEntity(this); +#endif + } /* @@ -1011,8 +1030,6 @@ player::SendEntity(entity ePEnt, float fChanged) // no need to send again until this flag is set freshly. this.completeInventorySend = FALSE; - - return (1); } @@ -1025,7 +1042,9 @@ void player::SendEntity_ary_myWeapons(int i){ //WriteByte(MSG_ENTITY, ary_myWeapons[i].iSlots ); WriteByte(MSG_ENTITY, ary_myWeapons[i].iClipLeft ); WriteByte(MSG_ENTITY, ary_myWeapons[i].iClipAkimboLeft ); +#if OTHER_PREDICTION_TEST != 1 WriteByte(MSG_ENTITY, ary_myWeapons[i].iBitsUpgrade_on ); +#endif WriteByte(MSG_ENTITY, ary_myWeapons[i].iFireMode ); WriteByte(MSG_ENTITY, ary_myWeapons[i].iFireModeAkimbo ); WriteByte(MSG_ENTITY, ary_myWeapons[i].iIronSight );