Game Development Community

dev|Pro Game Development Curriculum

Synchronized Random Number Generator

by Danni · 04/17/2011 (5:16 pm) · 2 comments

This code use useful for creating random numbers client side during prediction, then having the server perform the same generation so for example, you can fire hitscans client side spawning bullet puffs on the wall instantly, have the server replicate and send the result to everyone else.

This modification will also send verification of the servers current pointer in the list in the case of a lost packet, etc.

This uses a similar RNG table as Doom II with a few changes and is not intended to be secure or offer a flat distribution. Feel free to upgrade it if you need something different.

File: mods/syncedRandom.hpp
#ifndef __SYNCEDRANDOM_HPP_
#define __SYNCEDRANDOM_HPP_

// Doom2 Inspired static psudorandom number generator.
const static U8 rndtable[256] = {
	1,		8,		109,	220,	222,	241,	149,	107,	75,		248,	254,	140,	16,		66,
	74,		21,		211,	47,		80,		242,	154,	27,		205,	128,	161,	89,		77,		36,
	95,		110,	85,		48,		212,	140,	211,	249,	22,		79,		200,	50,		28,		188,
	52,		140,	202,	120,	68,		146,	62,		70,		184,	190,	91,		197,	152,	224,
	149,	104,	25,		178,	252,	182,	202,	182,	141,	197,	4,		81,		181,	242,
	145,	42,		39,		227,	156,	198,	225,	193,	219,	93,		122,	175,	249,	0,
	175,	143,	70,		239,	46,		246,	163,	53,		163,	109,	168,	135,	2,		235,
	25,		92,		20,		145,	138,	77,		69,		166,	78,		176,	173,	212,	166,	113,
	94,		161,	41,		50,		239,	49,		111,	164,	70,		60,		2,		37,		171,	75,
	136,	156,	11,		56,		42,		147,	138,	229,	73,		146,	77,		61,		98,		196,
	135,	106,	63,		197,	195,	86,		96,		203,	113,	101,	170,	247,	181,	113,
	80,		250,	108,	7,		255,	237,	129,	226,	79,		107,	112,	166,	103,	241,
	24,		223,	239,	120,	198,	58,		60,		82,		128,	3,		184,	66,		143,	224,
	145,	224,	81,		206,	163,	45,		63,		90,		168,	114,	59,		33,		159,	95,
	28,		139,	123,	98,		125,	196,	15,		70,		194,	253,	54,		14,		109,	226,
	71,		17,		161,	93,		186,	87,		244,	138,	20,		52,		123,	251,	26,		36,
	17,		46,		52,		231,	232,	76,		31,		221,	84,		37,		216,	165,	212,	106,
	197,	242,	98,		43,		39,		175,	254,	145,	190,	84,		118,	222,	187,	136,
	120,	163,	236,	249
};

class SyncedRandom
{
public:
	inline SyncedRandom()					{ index = 0; dirty = false;	};
	inline void		setPointer(U8 _index)	{ index = _index;			};
	inline U8		getPointer()			{ return index;				};

	inline U8		getRandom()				{ dirty = true; 
											  return rndtable[++index];	};

	inline bool		isDirty()				{ return dirty;				};

private:
	U8		index;
	bool	dirty;
};

#endif

Modification: shapeBase.cpp
void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream)

...

   if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask | MeshHiddenMask |
         ThreadMask | ImageMask | HideCloakMask | MountedMask | InvincibleMask |
         ShieldMask | SkinMask)))
      return retMask;

   // [Danni] Client Weapons: Synced Random
   if (stream->writeFlag(syncedRandom.isDirty() || mask & InitialUpdateMask))
	   stream->writeInt(syncedRandom.getPointer(), 8);
   // [Danni] END Client Weapons: Synced Random

   if (stream->writeFlag(mask & DamageMask)) {
      stream->writeFloat(mClampF(mDamage / mDataBlock->maxDamage, 0.f, 1.f), DamageLevelBits);
      stream->writeInt(mDamageState,NumDamageStateBits);
      stream->writeNormalVector( damageDir, 8 );
   }


Modification: shapeBase.cpp
void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream)
...
   if(!stream->readFlag())
      return;

   // [Danni] Client Weapons: Synced Random
   if (stream->readFlag())
	   syncedRandom.setPointer(stream->readInt(8));
   // [Danni] END Client Weapons: Synced Random

   if (stream->readFlag()) {

Modification: shapeBase.cpp (add this somewhere)
// [Danni] Client Weapons: Synced Random
ConsoleMethod(ShapeBase, getRandom, S32, 2,2, "Returns a random number between 0 and 255")
{
   return object->syncedRandom.getRandom();
}
// [Danni] END Client Weapons: Synced Random


Example usage: (NOTE: my T3D is modified to run weapon scripts client and server side; this code won't work with stock but gives an example)
for (%i = 0; %i < 20; %i++)
    {
      %damage = 5*(%obj.getRandom()%3+1);
      %muzzzleVector = %obj.getMuzzleVector(%slot);
      %muzzzleVector = VectorNormalize(%muzzzleVector);
      %yaw   = YawFromVector(%muzzzleVector);
      %pitch = PitchFromVector(%muzzzleVector);
      %yaw   += mDegToRad(((((%obj.getRandom()-128) << 19) / 65536.0)/65536.0)*360);
      %pitch += mDegToRad(((((%obj.getRandom()-128) << 18) / 65536.0)/65536.0)*360);
      %rnd = %rnd @ %yaw @ %pitch;
	  %muzzzleVector = VectorFromAngles(%yaw, %pitch);
      %muzzzleVector = VectorScale(%muzzzleVector, %this.ballisticsRange);
      %muzzlePoint = %obj.getMuzzlePoint(%slot); 
      %distance = VectorAdd(%muzzlePoint, %muzzzleVector);

      // Do a "hitscan"
      %scanTarg = ContainerRayCast(%muzzlePoint, %distance, %searchMasks, %obj); 
      if(%scanTarg) 
      { 
        %target = firstWord(%scanTarg);
        %target.damage(%obj,%pos,%damage,%this.meansOfDeath);
      }
    }

#1
05/04/2011 (11:42 am)
Nice resource, but in my opinion it is never good to let clients do this sort of thing. It opens you up to hacking attempts. It is better to have this taken care of server-side.
#2
06/03/2011 (8:08 pm)
It is taken care of server side; client side is purely cosmetic so if they hack it, they are only fooling themselves.