Commits

Teemu Piippo committed 702d9ae

Network variables take 2: now with proper compiler support and all.

  • Participants
  • Parent commits a0d66a3

Comments (0)

Files changed (6)

 		client_AnnouncerSound( pByteStream );
 		break;
 
+	// [Dusk]
+	case SVC_SETACSVARIABLE:
+		{
+			const ULONG ulSelector = NETWORK_ReadByte ( pByteStream );
+			const ULONG ulVarIdx = NETWORK_ReadByte ( pByteStream );
+			ULONG ulArrayIdx, ulBehaviorIdx;
+			FBehavior* module;
+			
+			// Array selectors transmit the array index too
+			if ( ACS_IsArraySelector( ulSelector ) )
+				ulArrayIdx = NETWORK_ReadLong ( pByteStream );
+			
+			// Mapvar selectors transmit behavior index
+			if ( ulSelector == NET_ACSMAPVAR || ulSelector == NET_ACSMAPARRAY ) {
+				ulBehaviorIdx = NETWORK_ReadLong ( pByteStream );
+				
+				// Find the module by this index
+				module = FBehavior::StaticGetModule (ulBehaviorIdx);
+				if (!module) {
+					// Read in what should be lValue
+					NETWORK_ReadLong ( pByteStream );
+					break;
+				}
+			}
+			
+			const LONG lValue = NETWORK_ReadLong ( pByteStream );
+			
+			// Set the variable
+			switch( ulSelector ) {
+			case NET_ACSMAPVAR:
+				Printf ("Recieved value %ld for MAP VAR %lu\n", lValue, ulVarIdx);
+				*(module->MapVars[ulVarIdx]) = lValue;
+				break;
+			case NET_ACSWORLDVAR:
+				Printf ("Recieved value %ld for WORLD VAR %lu\n", lValue, ulVarIdx);
+				ACS_WorldVars[ulVarIdx] = lValue;
+				break;
+			case NET_ACSGLOBALVAR:
+				Printf ("Recieved value %ld for GLOBAL VAR %lu\n", lValue, ulVarIdx);
+				ACS_GlobalVars[ulVarIdx] = lValue;
+				break;
+			case NET_ACSMAPARRAY:
+				Printf ("Recieved value %ld for MAP ARRAY %lu:%lu\n", lValue, ulVarIdx, ulArrayIdx);
+				module->SetArrayVal (ulVarIdx, ulArrayIdx, lValue);
+				break;
+			case NET_ACSWORLDARRAY:
+				Printf ("Recieved value %ld for WORLD ARRAY %lu:%lu\n", lValue, ulVarIdx, ulArrayIdx);
+				ACS_WorldArrays[ulVarIdx][ulArrayIdx] = lValue;
+				break;
+			case NET_ACSGLOBALARRAY:
+				Printf ("Recieved value %ld for GLOBAL ARRAY %lu:%lu\n", lValue, ulVarIdx, ulArrayIdx);
+				ACS_GlobalArrays[ulVarIdx][ulArrayIdx] = lValue;
+				break;
+			}
+			break;
+		}
+
 	case SVC_EXTENDEDCOMMAND:
 		{
 			const LONG lExtCommand = NETWORK_ReadByte( pByteStream );

src/network_enums.h

 	ENUM_ELEMENT ( SVC_DOPUSHER ),
 	ENUM_ELEMENT ( SVC_ADJUSTPUSHER ),
 	ENUM_ELEMENT ( SVC_ANNOUNCERSOUND ),
+	ENUM_ELEMENT ( SVC_SETACSVARIABLE ),
 	ENUM_ELEMENT ( SVC_EXTENDEDCOMMAND ),
 
 	ENUM_ELEMENT ( NUM_SERVER_COMMANDS ),
 {
 	DWORD ArraySize;
 	SDWORD *Elements;
+	
+	// [Dusk] Network variable stuff
+	SDWORD *LastElements; // element data from the last tic
+	int Index; // the index of this variable
 };
 
 TArray<FBehavior *> FBehavior::StaticModules;
 SDWORD ACS_GlobalVars[NUM_GLOBALVARS];
 FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS];
 
+// [Dusk] The values the above arrays had the previous tic.
+SDWORD ACS_LastWorldVars[NUM_WORLDVARS];
+SDWORD ACS_LastGlobalVars[NUM_GLOBALVARS];
+FWorldGlobalArray ACS_LastWorldArrays[NUM_WORLDVARS];
+FWorldGlobalArray ACS_LastGlobalArrays[NUM_GLOBALVARS];
+
 
 //============================================================================
 //
 	return StaticModules[lib];
 }
 
+// ============================================================================
+// [Dusk] Get index by module
+int FBehavior::StaticModuleIndex (FBehavior* module) {
+	for (unsigned int i = 0; i < StaticModules.Size (); ++i)
+		if (StaticModules[i] == module)
+			return i;
+	return -1;
+}
+
+// ============================================================================
+// [Dusk] Update network variables to clients.
+void FBehavior::SyncNetworkVariables () {
+	int behaviorIndex = StaticModuleIndex (this);
+	
+	for (unsigned i = 0; i < NUM_MAPVARS; i++) {
+		SDWORD* var = MapVars[i];
+		if (!var || !NetMapVars[i] || *var == LastMapVars[i])
+			continue;
+		
+		SERVERCOMMANDS_SetACSVariable (NET_ACSMAPVAR, i, 0, behaviorIndex, *var);
+		LastMapVars[i] = *var;
+	}
+	
+	for (int i = 0; i < NumArrays; i++) {
+		const ArrayInfo* array = Arrays[i];
+		if (!array || !NetMapVars[array->Index])
+			continue;
+		
+		Printf ("%d <-> %d\n", i, array->Index);
+		
+		for (unsigned int j = 0; j < array->ArraySize; j++) {
+			if (array->Elements[j] != array->LastElements[j]) {
+				SERVERCOMMANDS_SetACSVariable (NET_ACSMAPARRAY, array->Index, j, behaviorIndex, array->Elements[j]);
+				array->LastElements[j] = array->Elements[j];
+			}
+		}
+	}
+}
+
 void FBehavior::StaticSerializeModuleStates (FArchive &arc)
 {
 	DWORD modnum;
 	memset (MapVarStore, 0, sizeof(MapVarStore));
 	ModuleName[0] = 0;
 
+	// [Dusk] Init stuff needed for network variables
+	memset (LastMapVars, 0, sizeof(LastMapVars));
+	memset (NetMapVars, 0, sizeof(NetMapVars));
+
 	// Now that everything is set up, record this module as being among the loaded modules.
 	// We need to do this before resolving any imports, because an import might (indirectly)
 	// need to resolve exports in this module. The only things that can be exported are
 				ArrayStore[i].ArraySize = LittleLong(chunk[3+i*2]);
 				ArrayStore[i].Elements = new SDWORD[ArrayStore[i].ArraySize];
 				memset(ArrayStore[i].Elements, 0, ArrayStore[i].ArraySize*sizeof(DWORD));
+
+				// [Dusk] Network variable stuff
+				ArrayStore[i].Index = chunk[2+i*2];
+				ArrayStore[i].LastElements = new SDWORD[ArrayStore[i].ArraySize];
+				memset(ArrayStore[i].LastElements, 0, ArrayStore[i].ArraySize*sizeof(DWORD));
 			}
 		}
 
 				}
 			}
 		}
+
+		// [Dusk] Load network variable info. What vars are net and what are not?
+		if ((chunk = (DWORD*)FindChunk (MAKE_ID ('N','E','T','V')))) {
+			// -- Map vars
+			int m_num = LittleLong (chunk[1]);
+			for (int i = 0; i < m_num; i++)
+				NetMapVars[chunk[2 + i]] = true;
+			
+			/*
+			// -- World vars
+			int w_num = LittleLong (chunk[2 + m_num]);
+			
+			// -- Global vars
+			int g_num = LittleLong (chunk[3 + m_num + w_num]);
+			Printf ("NETV chunk has %d map vars, %d world vars and %d global vars\n",
+				m_num, w_num, g_num);
+			*/
+		}
 	}
 
 	DPrintf ("Loaded %d scripts, %d functions\n", NumScripts, NumFunctions);
 			{
 				delete[] ArrayStore[i].Elements;
 				ArrayStore[i].Elements = NULL;
+
+				// [Dusk] Delete LastElements
+				delete[] ArrayStore[i].LastElements;
+				ArrayStore[i].LastElements = NULL;
 			}
 		}
 		delete[] ArrayStore;
 	localvars = NULL;
 }
 
+// [Dusk] Send network variable information to clients
+void DLevelScript::UpdateNetworkVariables () {
+	// Only the server can update any variables to anybody.
+	if (NETWORK_GetState() != NETSTATE_SERVER)
+		return;
+	
+	// Find the active behavior index
+	int behaviorIndex = activeBehavior->StaticModuleIndex (activeBehavior);
+	
+	// Shouldn't happen.
+	if (behaviorIndex == -1)
+		I_Error ("DLevelScript::UpdateNetworkVariables: Could not find active behavior index!");
+	
+	ULONG ulBehaviorIndex = static_cast<ULONG> (behaviorIndex);
+	
+	// ========================================================================
+	// Behavior-specific map vars
+	activeBehavior->SyncNetworkVariables ();
+	
+	// ========================================================================
+	// World vars
+	for (unsigned int i = 0; i < NUM_WORLDVARS; i++) {
+		if (ACS_WorldVars[i] != ACS_LastWorldVars[i]) {
+			SERVERCOMMANDS_SetACSVariable (NET_ACSWORLDVAR, i, 0, ulBehaviorIndex, ACS_WorldVars[i]);
+			ACS_LastWorldVars[i] = ACS_WorldVars[i];
+		}
+		
+		// World arrays
+		TMapIterator<SDWORD, SDWORD, FWorldGlobalArray> it (ACS_WorldArrays[i]);
+		FWorldGlobalArray::Pair* pair;
+		while (it.NextPair (pair)) {
+			if (pair->Value != ACS_LastWorldArrays[i][pair->Key]) {
+				SERVERCOMMANDS_SetACSVariable (NET_ACSWORLDARRAY, i, pair->Key, ulBehaviorIndex, pair->Value);
+				ACS_LastWorldArrays[i][pair->Key] = pair->Value;
+			}
+		}
+	}
+	
+	// ========================================================================
+	// Global vars
+	for (unsigned int i = 0; i < NUM_GLOBALVARS; i++) {
+		if (ACS_GlobalVars[i] != ACS_LastGlobalVars[i]) {
+			SERVERCOMMANDS_SetACSVariable (NET_ACSGLOBALVAR, i, 0, ulBehaviorIndex, ACS_GlobalVars[i]);
+			ACS_LastGlobalVars[i] = ACS_GlobalVars[i];
+		}
+		
+		// Global arrays
+		TMapIterator<SDWORD, SDWORD, FWorldGlobalArray> it (ACS_GlobalArrays[i]);
+		FWorldGlobalArray::Pair* pair;
+		while (it.NextPair (pair)) {
+			if (pair->Value != ACS_LastGlobalArrays[i][pair->Key]) {
+				SERVERCOMMANDS_SetACSVariable (NET_ACSGLOBALARRAY, i, pair->Key, ulBehaviorIndex, pair->Value);
+				ACS_LastGlobalArrays[i][pair->Key] = pair->Value;
+			}
+		}
+	}
+}
+
+bool ACS_IsArraySelector (ULONG ulSelector)
+{
+	return ulSelector == NET_ACSMAPARRAY ||
+		ulSelector == NET_ACSWORLDARRAY ||
+		ulSelector == NET_ACSGLOBALARRAY;
+}
+
 void DLevelScript::Unlink ()
 {
 	DACSThinker *controller = DACSThinker::ActiveThinker;
 	// [BB] Stop the net traffic measurement and add the result to this script's traffic.
 	NETTRAFFIC_AddACSScriptTraffic ( script, NETWORK_StopTrafficMeasurement ( ) );
 
+	// [Dusk] Send any network variable information that needs to be sent now.
+	UpdateNetworkVariables ();
+
 	return resultValue;
 }
 
 	NUM_GLOBALVARS = 64
 };
 
+// [Dusk] SVC_SETACSVARIABLE selectors
+enum
+{
+	NET_ACSMAPVAR,
+	NET_ACSWORLDVAR,
+	NET_ACSGLOBALVAR,
+	NET_ACSMAPARRAY,
+	NET_ACSWORLDARRAY,
+	NET_ACSGLOBALARRAY,
+};
+
 struct InitIntToZero
 {
 	void Init(int &v)
 
 	SDWORD *MapVars[NUM_MAPVARS];
 
+	// [Dusk]
+	SDWORD LastMapVars[NUM_MAPVARS];
+	bool NetMapVars[NUM_MAPVARS];
+
 	static FBehavior *StaticLoadModule (int lumpnum, FileReader * fr=NULL, int len=0);
 	static void StaticLoadDefaultModules ();
 	static void StaticUnloadModules ();
 	static void StaticStopMyScripts (AActor *actor);
 	static int StaticCountTypedScripts( WORD type );
 
+	// [Dusk] Get index by module
+	int StaticModuleIndex (FBehavior* module);
+
+	// [Dusk] Synchronize network variables
+	void SyncNetworkVariables ();
+
 private:
 	struct ArrayInfo;
 
 	int LineFromID(int id);
 	int SideFromID(int id, int side);
 
+	void UpdateNetworkVariables (); // [Dusk]
+
 private:
 	DLevelScript ();
 
 // [BB] Export DoGiveInv
 bool	DoGiveInv(AActor *actor, const PClass *info, int amount);
 
+bool	ACS_IsArraySelector (ULONG ulSelector);
+
 #endif //__P_ACS_H__

src/sv_commands.cpp

 }
 
 //*****************************************************************************
+// [Dusk] Tells the client to set an ACS variable to a certain value
+void SERVERCOMMANDS_SetACSVariable ( ULONG ulSelector, ULONG ulVarIdx, ULONG ulArrayIdx, ULONG ulBehaviorIdx, LONG lValue, ULONG ulPlayerExtra, ULONG ulFlags )
+{
+	NetCommand command ( SVC_SETACSVARIABLE );
+	command.addByte ( ulSelector );
+	command.addByte ( ulVarIdx );
+
+	// Only send the array index if the selector is an array one to save bandwidth.
+	if ( ACS_IsArraySelector( ulSelector ) )
+		command.addLong ( ulArrayIdx );
+
+	// Likewise, only send the behavior index for map variables. World/global vars do not need it.
+	if ( ulSelector == NET_ACSMAPVAR || ulSelector == NET_ACSMAPARRAY )
+		command.addLong ( ulBehaviorIdx );
+
+	command.addLong ( lValue );
+	command.sendCommandToClients ( ulPlayerExtra, ulFlags );
+}
+
+//*****************************************************************************
 //*****************************************************************************
 //
 void SERVERCOMMANDS_SetSideFlags( ULONG ulSide, ULONG ulPlayerExtra, ULONG ulFlags )

src/sv_commands.h

 
 // ACS commands. These have something to do with ACS scripts.
 void	SERVERCOMMANDS_ACSScriptExecute( ULONG ulScript, AActor *pActivator, LONG lLineIdx, char *pszMap, bool bBackSide, int iArg0, int iArg1, int iArg2, bool bAlways, ULONG ulPlayerExtra = MAXPLAYERS, ULONG ulFlags = 0 );
+void	SERVERCOMMANDS_SetACSVariable( ULONG ulSelector, ULONG ulVarIdx, ULONG ulArrayIdx, ULONG ulModuleIdx, LONG lValue, ULONG ulPlayerExtra = MAXPLAYERS, ULONG ulFlags = 0 );
 
 // Sound commands. These play a sound.
 void	SERVERCOMMANDS_Sound( LONG lChannel, const char *pszSound, float fVolume, float fAttenuation, ULONG ulPlayerExtra = MAXPLAYERS, ULONG ulFlags = 0 );