First commit

This commit is contained in:
Anton Tarasenko 2020-02-16 19:53:59 +07:00
commit 5b48414900
263 changed files with 24830 additions and 0 deletions

View file

@ -0,0 +1,126 @@
//==============================================================================
// NicePack / NiceClientData
//==============================================================================
// Adds data interface relevant only to client,
// as well as client implementation of more general functions.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceClientData extends NiceData
config(NicePack);
var protected NiceStorageClient ownerStorage;
// Server has yet unsent changes to this data
// (according to latest information from server)
var protected bool _isUpToDate;
// We can currently send server changes in this data
var protected bool _hasWriteRights;
static function NiceData NewData(string newID){
local NiceData newData;
newData = new class'NiceClientData';
newData.ID = newID;
return newData;
}
// #private
function SetOwnerStorage( NiceRemoteHack.DataRef dataRef,
NiceStorageClient newOwner){
if(ID ~= class'NiceRemoteHack'.static.GetDataRefID(dataRef))
ownerStorage = newOwner;
}
function bool IsUpToDate(){
return _isUpToDate;
}
// #private
function SetUpToDate(NiceRemoteHack.DataRef dataRef, bool newStatus){
if(ID ~= class'NiceRemoteHack'.static.GetDataRefID(dataRef))
_isUpToDate = newStatus;
}
function bool HasWriteRights(){
return _hasWriteRights;
}
// #private
function SetWriteRights(NiceRemoteHack.DataRef dataRef, bool newRights){
if(ID ~= class'NiceRemoteHack'.static.GetDataRefID(dataRef))
_hasWriteRights = newRights;
}
//==============================================================================
// > Setter / getters for variables that perform necessary synchronization
function SetByte(string variableName, byte variableValue){
if(!HasWriteRights()) return;
if(ownerStorage == none) return;
if(ownerStorage.remoteRI == none) return;
_SetByte(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallByteVariableUpdated(ID, variableName, variableValue);
ownerStorage.remoteRI.ServerSendByte(V(ID, variableName), variableValue);
}
function SetInt(string variableName, int variableValue){
if(!HasWriteRights()) return;
if(ownerStorage == none) return;
if(ownerStorage.remoteRI == none) return;
_SetInt(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallIntVariableUpdated(ID, variableName, variableValue);
ownerStorage.remoteRI.ServerSendInt(V(ID, variableName), variableValue);
}
function SetBool(string variableName, bool variableValue){
if(!HasWriteRights()) return;
if(ownerStorage == none) return;
if(ownerStorage.remoteRI == none) return;
_SetBool(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallBoolVariableUpdated(ID, variableName, variableValue);
ownerStorage.remoteRI.ServerSendBool(V(ID, variableName), variableValue);
}
function SetFloat(string variableName, float variableValue){
if(!HasWriteRights()) return;
if(ownerStorage == none) return;
if(ownerStorage.remoteRI == none) return;
_SetFloat(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallFloatVariableUpdated(ID, variableName, variableValue);
ownerStorage.remoteRI.ServerSendFloat(V(ID, variableName), variableValue);
}
function SetString(string variableName, string variableValue){
if(!HasWriteRights()) return;
if(ownerStorage == none) return;
if(ownerStorage.remoteRI == none) return;
_SetString(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallStringVariableUpdated(ID, variableName, variableValue);
ownerStorage.remoteRI.ServerSendString(V(ID, variableName), variableValue);
}
function SetClass(string variableName, class<Actor> variableValue){
if(!HasWriteRights()) return;
if(ownerStorage == none) return;
if(ownerStorage.remoteRI == none) return;
_SetClass(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallClassVariableUpdated(ID, variableName, variableValue);
ownerStorage.remoteRI.ServerSendClass(V(ID, variableName), variableValue);
}
defaultproperties
{
}

243
sources/Data/NiceData.uc Normal file
View file

@ -0,0 +1,243 @@
//==============================================================================
// NicePack / NiceData
//==============================================================================
// Base class for remote data, defines basic interface,
// used by both server and client storages.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceData extends NiceRemoteHack
abstract
config(NicePack);
var const class<NiceRemoteDataEvents> events;
enum EValueType{
VTYPE_BOOL,
VTYPE_BYTE,
VTYPE_INT,
VTYPE_FLOAT,
VTYPE_STRING,
VTYPE_CLASS,
VTYPE_NULL // Variable doesn't exist (in this storage)
};
struct Variable{
var protected string myName;
// Value of what type is currently stored in this struct
var protected EValueType currentType;
// Containers for various value types
var protected byte storedByte;
var protected int storedInt;
var protected bool storedBool;
var protected float storedFloat;
var protected string storedString;
var protected class<Actor> storedClass;
};
enum EDataPriority{ // Data change messages from server to client are...
// ...sent immediately;
NSP_REALTIME,
// ...sent with time intervals between them;
NSP_HIGH,
// ...sent with time intervals between them,
// but only if high-priority queue is empty.
NSP_LOW
// Data change messages from clients are always sent immediately.
};
var protected string ID;
var protected array<Variable> variables;
static function NiceData NewData(string newID){
local NiceData newData;
newData = new class'NiceData';
newData.ID = newID;
return newData;
}
function string GetID(){
return ID;
}
function bool IsEmpty(){
return variables.length <= 0;
}
function EValueType GetVariableType(string variableName){
local int index;
index = GetVariableIndex(variableName);
if(index < 0)
return VTYPE_NULL;
return variables[index].currentType;
}
function array<string> GetVariableNames(){
local int i;
local array<string> mapResult;
for(i = 0;i < variables.length;i ++)
mapResult[i] = variables[i].myName;
return mapResult;
}
protected function int GetVariableIndex(string variableName){
local int i;
for(i = 0;i < variables.length;i ++)
if(variables[i].myName ~= variableName)
return i;
return -1;
}
//==============================================================================
// > Setter / getters for variables that perform necessary synchronization.
function SetByte(string variableName, byte variableValue);
function byte GetByte(string variableName, optional byte defaultValue){
local int index;
index = GetVariableIndex(variableName);
if(index < 0)
return defaultValue;
if(variables[index].currentType != EValueType.VTYPE_BYTE)
return defaultValue;
return variables[index].storedByte;
}
function SetInt(string variableName, int variableValue);
function int GetInt(string variableName, optional int defaultValue){
local int index;
index = GetVariableIndex(variableName);
if(index < 0)
return defaultValue;
if(variables[index].currentType != EValueType.VTYPE_INT)
return defaultValue;
return variables[index].storedInt;
}
function SetBool(string variableName, bool variableValue);
function bool GetBool(string variableName, optional bool defaultValue){
local int index;
index = GetVariableIndex(variableName);
if(index < 0)
return defaultValue;
if(variables[index].currentType != EValueType.VTYPE_BOOL)
return defaultValue;
return variables[index].storedBool;
}
function SetFloat(string variableName, float variableValue);
function float GetFloat(string variableName, optional float defaultValue){
local int index;
index = GetVariableIndex(variableName);
if(index < 0)
return defaultValue;
if(variables[index].currentType != EValueType.VTYPE_FLOAT)
return defaultValue;
return variables[index].storedFloat;
}
function SetString(string variableName, string variableValue);
function string GetString(string variableName, optional string defaultValue){
local int index;
index = GetVariableIndex(variableName);
if(index < 0)
return defaultValue;
if(variables[index].currentType != EValueType.VTYPE_STRING)
return defaultValue;
return variables[index].storedString;
}
function SetClass(string variableName, class<Actor> variableValue);
function class<Actor> GetClass( string variableName,
optional class<Actor> defaultValue){
local int index;
index = GetVariableIndex(variableName);
if(index < 0)
return defaultValue;
if(variables[index].currentType != EValueType.VTYPE_CLASS)
return defaultValue;
return variables[index].storedClass;
}
//==============================================================================
// > Setter that records variables locally, without any synchronization work.
// #private
function _SetByte(DataRef dataRef, byte variableValue){
local int index;
local Variable newValue;
newValue.myName = dataRef.variable;
newValue.storedByte = variableValue;
newValue.currentType = VTYPE_BYTE;
index = GetVariableIndex(dataRef.variable);
if(index < 0)
index = variables.length;
variables[index] = newValue;
}
function _SetInt(DataRef dataRef, int variableValue){
local int index;
local Variable newValue;
newValue.myName = dataRef.variable;
newValue.storedInt = variableValue;
newValue.currentType = VTYPE_INT;
index = GetVariableIndex(dataRef.variable);
if(index < 0)
index = variables.length;
variables[index] = newValue;
}
function _SetBool(DataRef dataRef, bool variableValue){
local int index;
local Variable newValue;
newValue.myName = dataRef.variable;
newValue.storedBool = variableValue;
newValue.currentType = VTYPE_BOOL;
index = GetVariableIndex(dataRef.variable);
if(index < 0)
index = variables.length;
variables[index] = newValue;
}
function _SetFloat(DataRef dataRef, float variableValue){
local int index;
local Variable newValue;
newValue.myName = dataRef.variable;
newValue.storedFloat = variableValue;
newValue.currentType = VTYPE_FLOAT;
index = GetVariableIndex(dataRef.variable);
if(index < 0)
index = variables.length;
variables[index] = newValue;
}
function _SetString(DataRef dataRef, string variableValue){
local int index;
local Variable newValue;
newValue.myName = dataRef.variable;
newValue.storedString = variableValue;
newValue.currentType = VTYPE_STRING;
index = GetVariableIndex(dataRef.variable);
if(index < 0)
index = variables.length;
variables[index] = newValue;
}
function _SetClass(DataRef dataRef, class<Actor> variableValue){
local int index;
local Variable newValue;
newValue.myName = dataRef.variable;
newValue.storedClass = variableValue;
newValue.currentType = VTYPE_CLASS;
index = GetVariableIndex(dataRef.variable);
if(index < 0)
index = variables.length;
variables[index] = newValue;
}
defaultproperties
{
events=class'NiceRemoteDataEvents'
}

View file

@ -0,0 +1,16 @@
//==============================================================================
// NicePack / NiceDataQueue
//==============================================================================
// Implements a queue of updates for data stored on a server.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceDataQueue extends Object
config(NicePack);
defaultproperties
{
}

View file

@ -0,0 +1,79 @@
//==============================================================================
// NicePack / NiceRemoteDataAdapter
//==============================================================================
// Temporary stand-in for future functionality.
// Use this class to catch events from storages.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceRemoteDataAdapter extends Object
dependson(NiceStorageBase);
var LevelInfo level;
static function DataCreated(string dataID);
// Called on clients the moment client storage connects to the server one.
static function LinkEstablished();
// Called on client after server responds to his request
// to check if certain data exists.
static function DataExistResponse(string dataID, bool doesExist);
// Called on client after server responds to his connection request.
static function ConnectionRequestResponse
( string dataID,
NiceStorageBase.ECreateDataResponse response
);
// Fired-off when writing rights to a certain data were granted.
// Always called on server.
// Only called on client that gained writing rights.
static function WriteAccessGranted( string dataID,
NicePlayerController newOwner);
// Fired-off when server refused to grant writing rights to data.
// Always called on server.
// Only called on client that tried to gain writing rights.
static function WriteAccessRevoked( string dataID,
NicePlayerController newOwner);
// Fired-off when writing rights to a certain data were revoked.
// Always called on server.
// Only called on client that lost writing rights.
static function WriteAccessRefused( string dataID,
NicePlayerController newOwner);
// Fired off on client when server finished sending him all the info about
// particular data set.
static function DataUpToDate(string dataID);
// Fire off on server and listening clients when
// a particular variable was updated.
static function VariableUpdated( string dataID,
string varName);
static function BoolVariableUpdated(string dataID,
string varName,
bool newValue);
static function ByteVariableUpdated(string dataID,
string varName,
byte newValue);
static function IntVariableUpdated( string dataID,
string varName,
int newValue);
static function FloatVariableUpdated( string dataID,
string varName,
float newValue);
static function StringVariableUpdated( string dataID,
string varName,
string newValue);
static function ClassVariableUpdated( string dataID,
string varName,
class<Actor> newValue);
defaultproperties
{
}

View file

@ -0,0 +1,149 @@
//==============================================================================
// NicePack / NiceRemoteDataEvents
//==============================================================================
// Temporary stand-in for future functionality.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceRemoteDataEvents extends Object
dependson(NiceStorageBase);
var array< class<NiceRemoteDataAdapter> > adapters;
// If adapter was already added also returns 'false'.
static function bool AddAdapter(class<NiceRemoteDataAdapter> newAdapter,
optional LevelInfo level){
local int i;
if(newAdapter == none) return false;
for(i = 0;i < default.adapters.length;i ++)
if(default.adapters[i] == newAdapter)
return false;
newAdapter.default.level = level;
default.adapters[default.adapters.length] = newAdapter;
return true;
}
// If adapter wasn't even present also returns 'false'.
static function bool RemoveAdapter(class<NiceRemoteDataAdapter> adapter){
local int i;
if(adapter == none) return false;
for(i = 0;i < default.adapters.length;i ++){
if(default.adapters[i] == adapter){
default.adapters.Remove(i, 1);
return true;
}
}
return false;
}
static function CallLinkEstablished(){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.LinkEstablished();
}
static function CallDataCreated(string dataID){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.DataCreated(dataID);
}
static function CallDataExistResponse(string dataID, bool doesExist){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.DataExistResponse(dataID, doesExist);
}
static function CallConnectionRequestResponse
( string dataID,
NiceStorageBase.ECreateDataResponse response
){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.ConnectionRequestResponse(dataID, response);
}
static function CallVariableUpdated(string dataID, string variableName){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.VariableUpdated(dataID, variableName);
}
static function CallBoolVariableUpdated(string dataID, string variableName,
bool newValue){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.BoolVariableUpdated( dataID, variableName,
newValue);
}
static function CallByteVariableUpdated(string dataID, string variableName,
byte newValue){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.ByteVariableUpdated( dataID, variableName,
newValue);
}
static function CallIntVariableUpdated( string dataID, string variableName,
int newValue){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.IntVariableUpdated( dataID, variableName,
newValue);
}
static function CallFloatVariableUpdated( string dataID, string variableName,
float newValue){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.FloatVariableUpdated(dataID, variableName,
newValue);
}
static function CallStringVariableUpdated( string dataID, string variableName,
string newValue){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.StringVariableUpdated( dataID,
variableName,
newValue);
}
static function CallClassVariableUpdated( string dataID, string variableName,
class<Actor> newValue){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.ClassVariableUpdated(dataID, variableName,
newValue);
}
static function CallDataUpToDate(string dataID){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.DataUpToDate(dataID);
}
static function CallWriteAccessGranted( string dataID,
NicePlayerController newOwner){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.WriteAccessGranted(dataID, newOwner);
}
static function CallWritingAccessRevoked( string dataID,
NicePlayerController newOwner){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.WriteAccessRevoked(dataID, newOwner);
}
static function CallWriteAccessRefused( string dataID,
NicePlayerController newOwner){
local int i;
for(i = 0;i < default.adapters.length;i ++)
default.adapters[i].static.WriteAccessRefused(dataID, newOwner);
}

View file

@ -0,0 +1,47 @@
//==============================================================================
// NicePack / NiceRemoteHack
//==============================================================================
// Structure introduced for simple 'hack':
// ~ We want our replication info class to call methods that
// we would otherwise mark as 'protected';
// ~ To make this possible we introduce this structure that would can only
// be filled with valid data (non-empty name of relevant data)
// by other protected methods of this class;
// ~ Methods that that we wish only replication info to access will accept
// this structure as a parameter and only function if
// it's filled with valid data.
// ~ This way users won't be able to actually make these methods
// do any work, but replication info, that will be called from within
// with valid 'DataRef' structure, will be able to invoke them.
// ~ In addition we add the ability for this structure
// to point at a specific variable.
// ~ Such variables are marked with '#private' in comments.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceRemoteHack extends Object
abstract;
struct DataRef{
var protected string ID;
var protected string variable;
};
// Creates validation structure for data set with a given name
protected function DataRef V(string ID, optional string variable){
local DataRef validRef;
validRef.ID = ID;
validRef.variable = variable;
return validRef;
}
static function string GetDataRefID(DataRef dataRef){
return dataRef.ID;
}
static function string GetDataRefVar(DataRef dataRef){
return dataRef.variable;
}

View file

@ -0,0 +1,258 @@
//==============================================================================
// NicePack / NiceRepInfoRemoteData
//==============================================================================
// Replication info class for replicating messages needed by Storage system.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceRepInfoRemoteData extends ReplicationInfo
dependson(NiceRemoteHack);
replication{
reliable if(Role == ROLE_Authority)
ClientConnectionResponse, ClientDataExistResponse,
ClientOpenWriteRights, ClientCloseWriteRights, ClientRefuseWriteRights;
reliable if(Role < ROLE_Authority)
ServerCreateData, ServerAddListener, ServerAskDataExist,
ServerRequestWriteAccess, ServerGiveupWriteAccess;
// For sending data
reliable if(Role == ROLE_Authority)
ClientSendBool, ClientSendByte, ClientSendInt, ClientSendFloat,
ClientSendString, ClientSendClass;
reliable if(Role < ROLE_Authority)
ServerSendBool, ServerSendByte, ServerSendInt, ServerSendFloat,
ServerSendString, ServerSendClass;
}
// These variables are needed in almost every function in this class,
// so they're declared in a scope of whole class and are setup via
// 'SetupVars' call.
var string dataID;
var string dataVarName;
var NiceData remoteData;
var NiceStorageBase storage;
var NiceStorageClient storageClient;
var NiceStorageServer storageServer;
var NicePlayerController ownerPlayer;
simulated function SetupVars(NiceRemoteHack.DataRef dataRef){
dataID = class'NiceRemoteHack'.static.GetDataRefID(dataRef);
dataVarName = class'NiceRemoteHack'.static.GetDataRefVar(dataRef);
storage = class'NicePack'.static.GetStorage(level);
ownerPlayer = NicePlayerController(owner);
if(level.netMode == NM_DedicatedServer)
storageServer = NiceStorageServer(storage);
else
storageClient = NiceStorageClient(storage);
if(storage != none)
remoteData = storage.GetData(dataID);
}
function ServerSendBool(NiceRemoteHack.DataRef dataRef, bool varValue){
SetupVars(dataRef);
if(remoteData == none) return;
remoteData.SetBool(dataVarName, varValue);
}
function ServerSendByte(NiceRemoteHack.DataRef dataRef, byte varValue){
SetupVars(dataRef);
if(remoteData == none) return;
remoteData.SetByte(dataVarName, varValue);
}
function ServerSendInt(NiceRemoteHack.DataRef dataRef, int varValue){
SetupVars(dataRef);
if(remoteData == none) return;
remoteData.SetInt(dataVarName, varValue);
}
function ServerSendFloat(NiceRemoteHack.DataRef dataRef, float varValue){
SetupVars(dataRef);
if(remoteData == none) return;
remoteData.SetFloat(dataVarName, varValue);
}
function ServerSendString(NiceRemoteHack.DataRef dataRef, string varValue){
SetupVars(dataRef);
if(remoteData == none) return;
remoteData.SetString(dataVarName, varValue);
}
function ServerSendClass(NiceRemoteHack.DataRef dataRef, class<Actor> varValue){
SetupVars(dataRef);
if(remoteData == none) return;
remoteData.SetClass(dataVarName, varValue);
}
simulated function ClientSendByte( NiceRemoteHack.DataRef dataRef,
byte varValue,
bool replicationFinished){
if(level.netMode == NM_DedicatedServer) return;
// Full 'SetupVars' is an overkill
storageClient = NiceStorageClient(class'NicePack'.static.GetStorage(level));
if(storageClient == none) return;
storageClient.CheckinByte(dataRef, varValue, replicationFinished);
}
simulated function ClientSendBool( NiceRemoteHack.DataRef dataRef,
bool varValue,
bool replicationFinished){
if(level.netMode == NM_DedicatedServer) return;
// Full 'SetupVars' is an overkill
storageClient = NiceStorageClient(class'NicePack'.static.GetStorage(level));
if(storageClient == none) return;
storageClient.CheckinBool(dataRef, varValue, replicationFinished);
}
simulated function ClientSendInt( NiceRemoteHack.DataRef dataRef,
int varValue,
bool replicationFinished){
if(level.netMode == NM_DedicatedServer) return;
// Full 'SetupVars' is an overkill
storageClient = NiceStorageClient(class'NicePack'.static.GetStorage(level));
if(storageClient == none) return;
storageClient.CheckinInt(dataRef, varValue, replicationFinished);
}
simulated function ClientSendFloat( NiceRemoteHack.DataRef dataRef,
float varValue,
bool replicationFinished){
if(level.netMode == NM_DedicatedServer) return;
// Full 'SetupVars' is an overkill
storageClient = NiceStorageClient(class'NicePack'.static.GetStorage(level));
if(storageClient == none) return;
storageClient.CheckinFloat(dataRef, varValue, replicationFinished);
}
simulated function ClientSendString(NiceRemoteHack.DataRef dataRef,
string varValue,
bool replicationFinished){
if(level.netMode == NM_DedicatedServer) return;
// Full 'SetupVars' is an overkill
storageClient = NiceStorageClient(class'NicePack'.static.GetStorage(level));
if(storageClient == none) return;
storageClient.CheckinString(dataRef, varValue, replicationFinished);
}
simulated function ClientSendClass( NiceRemoteHack.DataRef dataRef,
class<Actor> varValue,
bool replicationFinished){
if(level.netMode == NM_DedicatedServer) return;
// Full 'SetupVars' is an overkill
storageClient = NiceStorageClient(class'NicePack'.static.GetStorage(level));
if(storageClient == none) return;
storageClient.CheckinClass(dataRef, varValue, replicationFinished);
}
function ServerCreateData( NiceRemoteHack.DataRef dataRef,
NiceData.EDataPriority priority){
local NiceServerData serverData;
SetupVars(dataRef);
if(ownerPlayer == none) return;
if(storageServer == none) return;
if(storageServer.CreateData(dataID, priority))
ClientConnectionResponse(dataRef, NSCDR_CREATED, true);
else{
if(remoteData != none)
// We've failed to create new data because it already exists;
ClientConnectionResponse( dataRef, NSCDR_ALREADYEXISTS,
remoteData.IsEmpty());
else
// We've failed to create new data for some other reason.
ClientConnectionResponse(dataRef, NSCDR_DOESNTEXIST, true);
}
serverData = NiceServerData(remoteData);
if(serverData != none)
serverData.AddListener(ownerPlayer);
}
function ServerAddListener(NiceRemoteHack.DataRef dataRef){
local NiceServerData serverData;
SetupVars(dataRef);
if(ownerPlayer == none) return;
if(storageServer == none) return;
serverData = NiceServerData(remoteData);
if(serverData != none){
ClientConnectionResponse( dataRef, NSCDR_CONNECTED,
serverData.IsEmpty());
}
else
ClientConnectionResponse(dataRef, NSCDR_DOESNTEXIST, true);
if(serverData != none)
serverData.AddListener(ownerPlayer);
}
function ServerAskDataExist(NiceRemoteHack.DataRef dataRef){
SetupVars(dataRef);
if(storage == none) return;
ClientDataExistResponse(dataRef, storage.DoesDataExistLocally(dataID));
}
simulated function ClientDataExistResponse( NiceRemoteHack.DataRef dataRef,
bool doesExist){
if(level.netMode == NM_DedicatedServer) return;
SetupVars(dataRef);
if(storage == none) return;
storage.events.static.CallDataExistResponse(dataID, doesExist);
}
simulated function ClientConnectionResponse
(
NiceRemoteHack.DataRef dataRef,
NiceStorageBase.ECreateDataResponse response,
bool replicationFinished
){
if(level.netMode == NM_DedicatedServer) return;
SetupVars(dataRef);
if(storageClient == none) return;
if(response != NSCDR_DOESNTEXIST)
storageClient.CheckinData(dataRef, replicationFinished);
storageClient.events.static.CallConnectionRequestResponse(dataID, response);
}
simulated function ClientOpenWriteRights(NiceRemoteHack.DataRef dataRef){
local NiceClientData clientData;
if(level.netMode == NM_DedicatedServer) return;
SetupVars(dataRef);
clientData = NiceClientData(remoteData);
if(clientData == none)
return;
clientData.SetWriteRights(dataRef, true);
storageClient.events.static.CallWriteAccessGranted(dataID, ownerPlayer);
}
simulated function ClientCloseWriteRights(NiceRemoteHack.DataRef dataRef){
local NiceClientData clientData;
if(level.netMode == NM_DedicatedServer) return;
SetupVars(dataRef);
clientData = NiceClientData(remoteData);
if(clientData == none)
return;
clientData.SetWriteRights(dataRef, false);
storageClient.events.static.CallWritingAccessRevoked(dataID, ownerPlayer);
}
simulated function ClientRefuseWriteRights(NiceRemoteHack.DataRef dataRef){
if(level.netMode == NM_DedicatedServer) return;
SetupVars(dataRef);
storageClient.events.static.CallWriteAccessRefused(dataID, ownerPlayer);
}
function ServerRequestWriteAccess(NiceRemoteHack.DataRef dataRef){
SetupVars(dataRef);
if(storageServer == none) return;
storageServer.OpenWriteAccess(dataRef, ownerPlayer);
}
function ServerGiveupWriteAccess(NiceRemoteHack.DataRef dataRef){
SetupVars(dataRef);
if(storageServer == none) return;
storageServer.CloseWriteAccess(dataRef);
}
defaultproperties
{
}

View file

@ -0,0 +1,262 @@
//==============================================================================
// NicePack / NiceServerData
//==============================================================================
// Adds data interface relevant only to server,
// as well as server implementation of more general functions.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceServerData extends NiceData
config(NicePack);
var NiceStorageServer ownerStorage;
var EDataPriority priority;
// Priority should only be set once, otherwise it can lead to issues
var bool wasPrioritySet;
// List of players who've requested replication of relevant Data set
var array<NicePlayerController> listeners;
// We can currently send server changes in this data
var protected NicePlayerController writeRightsOwner;
// Only admin players can get writing access to this data
// (but once writing access is given
// it won't close until player closes it or disconnects)
var bool isAdminOnly;
static function NiceData NewData(string newID){
local NiceData newData;
newData = new class'NiceServerData';
newData.ID = newID;
return newData;
}
function EDataPriority GetPriority(){
return priority;
}
// #private
function SetOwnerStorage( NiceRemoteHack.DataRef dataRef,
NiceStorageServerBase newOwner){
if(ID ~= class'NiceRemoteHack'.static.GetDataRefID(dataRef))
ownerStorage = NiceStorageServer(newOwner);//NICETODO: temp hack
}
// #private
function SetPriority( NiceRemoteHack.DataRef dataRef,
EDataPriority newPriority){
if(wasPrioritySet) return;
if(ID ~= class'NiceRemoteHack'.static.GetDataRefID(dataRef)){
priority = newPriority;
wasPrioritySet = true;
}
}
function NicePlayerController GetWriteRightsOwner(){
return writeRightsOwner;
}
// #private
function SetWriteRightsOwner( NiceRemoteHack.DataRef dataRef,
NicePlayerController newOwner){
if(ID ~= class'NiceRemoteHack'.static.GetDataRefID(dataRef))
writeRightsOwner = newOwner;
}
// Add 'NicePlayerController' referencing a player that should
// start listening to changes in this data set
function AddListener(NicePlayerController niceClient){
local int i;
if(ownerStorage == none) return;
if(niceClient == none || niceClient.remoteRI == none) return;
// Make sure this client isn't already added
for(i = 0;i < listeners.length;i ++)
if(listeners[i] == niceClient)
return;
listeners[listeners.length] = niceClient;
ownerStorage.AddConnection(niceClient);
// Replicate all the current data to this client
if(priority == NSP_REALTIME)
ReplicateToClient(V(ID), niceClient);
else
ownerStorage.PushDataIntoQueue(V(ID), niceClient, priority);
}
function bool IsListener(NicePlayerController niceClient){
local int i;
if(niceClient == none) return false;
for(i = 0;i < listeners.length;i ++)
if(niceClient == listeners[i])
return true;
return false;
}
// When the client disconnects - references to it's PC become 'null'.
// This function gets gets rid of them.
// #private
function PurgeNullListeners(DataRef dataRef){
local int i;
local array<NicePlayerController> newListeners;
if(dataRef.ID != ID) return;
for(i = 0;i < listeners.length;i ++)
if(listeners[i] != none)
newListeners[newListeners.length] = listeners[i];
listeners = newListeners;
}
// #private
function ReplicateToClient( NiceRemoteHack.DataRef dataRef,
NicePlayerController nicePlayer){
local int i;
if(nicePlayer == none || nicePlayer.remoteRI == none) return;
// Replication is only finished with last variable
for(i = 0;i < variables.length - 1;i ++)
ReplicateVariableToClient(V(ID), variables[i].myName, nicePlayer, false);
ReplicateVariableToClient(V(ID), variables[variables.length - 1].myName,
nicePlayer, true);
}
// #private
function ReplicateVariableToAll(NiceRemoteHack.DataRef dataRef,
string variable){
local int i;
if(ID ~= class'NiceRemoteHack'.static.GetDataRefID(dataRef)){
for(i = 0;i < listeners.length;i ++)
ReplicateVariableToClient(V(ID), variable, listeners[i], true);
}
}
// Guaranteed to check that 'niceClient' and it's 'remoteRI' are '!= none'.
// #private
function ReplicateVariableToClient( NiceRemoteHack.DataRef dataRef,
string variable,
NicePlayerController niceClient,
bool replicationFinished){
local int index;
if(niceClient == none || niceClient.remoteRI == none) return;
index = GetVariableIndex(variable);
if(index < 0)
return;
// NICETODO: change replication function based on variable's type
switch(variables[index].currentType){
case VTYPE_BOOL:
niceClient.remoteRI.ClientSendBool( V(ID, variables[index].myName),
variables[index].storedBool,
replicationFinished);
break;
case VTYPE_BYTE:
niceClient.remoteRI.ClientSendBYTE( V(ID, variables[index].myName),
variables[index].storedByte,
replicationFinished);
break;
case VTYPE_INT:
niceClient.remoteRI.ClientSendInt( V(ID, variables[index].myName),
variables[index].storedInt,
replicationFinished);
break;
case VTYPE_FLOAT:
niceClient.remoteRI.ClientSendFloat(V(ID, variables[index].myName),
variables[index].storedFloat,
replicationFinished);
break;
case VTYPE_STRING:
niceClient.remoteRI.ClientSendString(V(ID, variables[index].myName),
variables[index].storedString,
replicationFinished);
break;
case VTYPE_CLASS:
niceClient.remoteRI.ClientSendClass(V(ID, variables[index].myName),
variables[index].storedClass,
replicationFinished);
break;
default:
break;
}
}
//==============================================================================
// > Setter / getters for variables that perform necessary synchronization
function SetByte(string variableName, byte variableValue){
if(writeRightsOwner != none) return;
_SetByte(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallByteVariableUpdated(ID, variableName, variableValue);
if(priority == NSP_REALTIME)
ReplicateVariableToAll(V(ID), variableName);
else
ownerStorage.PushRequestIntoQueues(V(ID), variableName, priority);
}
function SetInt(string variableName, int variableValue){
if(writeRightsOwner != none) return;
_SetInt(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallIntVariableUpdated(ID, variableName, variableValue);
if(priority == NSP_REALTIME)
ReplicateVariableToAll(V(ID), variableName);
else
ownerStorage.PushRequestIntoQueues(V(ID), variableName, priority);
}
function SetBool(string variableName, bool variableValue){
if(writeRightsOwner != none) return;
_SetBool(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallBoolVariableUpdated(ID, variableName, variableValue);
if(priority == NSP_REALTIME)
ReplicateVariableToAll(V(ID), variableName);
else
ownerStorage.PushRequestIntoQueues(V(ID), variableName, priority);
}
function SetFloat(string variableName, float variableValue){
if(writeRightsOwner != none) return;
_SetFloat(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallFloatVariableUpdated(ID, variableName, variableValue);
if(priority == NSP_REALTIME)
ReplicateVariableToAll(V(ID), variableName);
else
ownerStorage.PushRequestIntoQueues(V(ID), variableName, priority);
}
function SetString(string variableName, string variableValue){
if(writeRightsOwner != none) return;
_SetString(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallStringVariableUpdated(ID, variableName, variableValue);
if(priority == NSP_REALTIME)
ReplicateVariableToAll(V(ID), variableName);
else
ownerStorage.PushRequestIntoQueues(V(ID), variableName, priority);
}
function SetClass(string variableName, class<Actor> variableValue){
if(writeRightsOwner != none) return;
_SetClass(V(ID, variableName), variableValue);
events.static.CallVariableUpdated(ID, variableName);
events.static.CallClassVariableUpdated(ID, variableName, variableValue);
if(priority == NSP_REALTIME)
ReplicateVariableToAll(V(ID), variableName);
else
ownerStorage.PushRequestIntoQueues(V(ID), variableName, priority);
}
defaultproperties
{
wasPrioritySet=false
}

View file

@ -0,0 +1,51 @@
//==============================================================================
// NicePack / NiceStorageBase
//==============================================================================
// Basic storage interface for creating and fetching data instances,
// relevant on both client and server.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceStorageBase extends NiceRemoteHack
abstract
config(NicePack);
var const class<NiceRemoteDataEvents> events;
// Type of actor variables used on this storage to collect data
var const class<NiceData> dataClass;
// Data collected so far
var protected array<NiceData> localStorage;
enum ECreateDataResponse{
NSCDR_CONNECTED,
NSCDR_ALREADYEXISTS,
NSCDR_CREATED,
NSCDR_DOESNTEXIST
};
function bool CreateData(string ID, NiceData.EDataPriority priority);
function bool DoesDataExistLocally(string ID){
if(GetData(ID) == none)
return false;
return true;
}
function NiceData GetData(string ID){
local int i;
for(i = 0;i < localStorage.length;i ++){
if(localStorage[i] == none) continue;
if(localStorage[i].GetID() ~= ID)
return localStorage[i];
}
return none;
}
defaultproperties
{
dataClass=class'NiceData'
events=class'NiceRemoteDataEvents'
}

View file

@ -0,0 +1,191 @@
//==============================================================================
// NicePack / NiceStorageClient
//==============================================================================
// Implements storage methods relevant only to client.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceStorageClient extends NiceStorageBase
config(NicePack);
var NiceRepInfoRemoteData remoteRI;
function bool ConnectData(string ID){
if(ID == "") return false;
if(remoteRI == none) return false;
remoteRI.ServerAddListener(V(ID));
return true;
}
function bool IsLinkEstablished(){
return (remoteRI == none);
}
// Requests a creation of remote data storage on server.
function bool CreateData(string ID, NiceData.EDataPriority priority){
if(ID == "") return false;
if(remoteRI == none) return false;
if(DoesDataExistLocally(ID)) return false;
remoteRI.ServerCreateData(V(ID), priority);
return true;
}
// Checks if server has data with a given name.
// Responds via calling 'DataExistResponse' event.
function DoesDataExistOnServer(string dataID){
if(remoteRI == none) return;
if(DoesDataExistLocally(dataID))
events.static.CallDataExistResponse(dataID, true);
else
remoteRI.ServerAskDataExist(V(dataID));
}
// Must be already connected to data to do this
function bool RequestWriteAccess(string dataID){
if(remoteRI == none) return false;
if(!DoesDataExistLocally(dataID)) return false;
remoteRI.ServerRequestWriteAccess(V(dataID));
return true;
}
function bool GiveupWriteAccess(string dataID){
local NiceClientData data;
if(remoteRI == none) return false;
if(!DoesDataExistLocally(dataID)) return false;
data = NiceClientData(GetData(dataID));
if(data == none || !data.HasWriteRights())
return false;
data.SetWriteRights(V(dataID), false);
remoteRI.ServerGiveupWriteAccess(V(dataID));
return true;
}
// #private
function CheckinData(DataRef dataRef, bool replicationFinished){
local NiceClientData clientData;
// This shouldn't happen, but just in case
if(DoesDataExistLocally(dataRef.ID)) return;
// Create data as requested
clientData =
NiceClientData(class'NiceClientData'.static.NewData(dataRef.ID));
if(clientData == none)
return;
localStorage[localStorage.length] = clientData;
clientData.SetOwnerStorage(dataRef, self);
clientData.SetUpToDate(dataRef, replicationFinished);
}
// #private
function CheckinBool(DataRef dataRef, bool value, bool replicationFinished){
local NiceClientData clientData;
clientData = NiceClientData(GetData(dataRef.ID));
if(clientData == none)
return;
clientData.SetUpToDate(dataRef, replicationFinished);
clientData._SetBool(dataRef, value);
// Events
events.static.CallVariableUpdated(dataRef.ID, dataRef.variable);
events.static.CallBoolVariableUpdated(dataRef.ID, dataRef.variable, value);
if(replicationFinished)
events.static.CallDataUpToDate(dataRef.ID);
}
function CheckinByte(DataRef dataRef, byte value, bool replicationFinished){
local NiceClientData clientData;
clientData = NiceClientData(GetData(dataRef.ID));
if(clientData == none)
return;
clientData.SetUpToDate(dataRef, replicationFinished);
clientData._SetByte(dataRef, value);
// Events
events.static.CallVariableUpdated(dataRef.ID, dataRef.variable);
events.static.CallByteVariableUpdated(dataRef.ID, dataRef.variable, value);
if(replicationFinished)
events.static.CallDataUpToDate(dataRef.ID);
}
function CheckinInt(DataRef dataRef, int value, bool replicationFinished){
local NiceClientData clientData;
clientData = NiceClientData(GetData(dataRef.ID));
if(clientData == none)
return;
clientData.SetUpToDate(dataRef, replicationFinished);
clientData._SetInt(dataRef, value);
// Events
events.static.CallVariableUpdated(dataRef.ID, dataRef.variable);
events.static.CallIntVariableUpdated(dataRef.ID, dataRef.variable, value);
if(replicationFinished)
events.static.CallDataUpToDate(dataRef.ID);
}
function CheckinFloat(DataRef dataRef, float value, bool replicationFinished){
local NiceClientData clientData;
clientData = NiceClientData(GetData(dataRef.ID));
if(clientData == none)
return;
clientData.SetUpToDate(dataRef, replicationFinished);
clientData._SetFloat(dataRef, value);
// Events
events.static.CallVariableUpdated(dataRef.ID, dataRef.variable);
events.static.CallFloatVariableUpdated(dataRef.ID, dataRef.variable, value);
if(replicationFinished)
events.static.CallDataUpToDate(dataRef.ID);
}
function CheckinString(DataRef dataRef, string value, bool replicationFinished){
local NiceClientData clientData;
clientData = NiceClientData(GetData(dataRef.ID));
if(clientData == none)
return;
clientData.SetUpToDate(dataRef, replicationFinished);
clientData._SetString(dataRef, value);
// Events
events.static.CallVariableUpdated(dataRef.ID, dataRef.variable);
events.static.CallStringVariableUpdated(dataRef.ID, dataRef.variable,
value);
if(replicationFinished)
events.static.CallDataUpToDate(dataRef.ID);
}
function CheckinClass( DataRef dataRef, class<Actor> value,
bool replicationFinished){
local NiceClientData clientData;
clientData = NiceClientData(GetData(dataRef.ID));
if(clientData == none)
return;
clientData.SetUpToDate(dataRef, replicationFinished);
clientData._SetClass(dataRef, value);
// Events
events.static.CallVariableUpdated(dataRef.ID, dataRef.variable);
events.static.CallClassVariableUpdated(dataRef.ID, dataRef.variable, value);
if(replicationFinished)
events.static.CallDataUpToDate(dataRef.ID);
}
// NICETODO: to debug, remove later
function Print(NicePlayerController pc){
local int i, j;
local array<string> names;
for(i = 0;i < localStorage.length;i ++){
pc.ClientMessage("Data:"@localStorage[i].GetID());
names = localStorage[i].GetVariableNames();
for(j = 0;j < names.length;j ++){
pc.ClientMessage(">" @ names[j] @ " = " @ String(localStorage[i].GetInt(names[j])));
}
}
}
defaultproperties
{
dataClass=class'NiceClientData'
}

View file

@ -0,0 +1,224 @@
//==============================================================================
// NicePack / NiceStorageServer
//==============================================================================
// Implements queue-related storage methods relevant only to server.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceStorageServer extends NiceStorageServerBase
config(NicePack);
function bool CanGrantWriteRights( NiceServerData data,
NicePlayerController clientRef){
if(!super.CanGrantWriteRights(data, clientRef))
return false;
if(HasPendingChanges(data.GetID(), clientRef))
return false;
return true;
}
// Checks if given data has some changes not yet replicated to the player.
// Works only on a server.
function bool HasPendingChanges(string dataID,
NicePlayerController nicePlayer){
local NiceServerData dataToCheck;
local int connectionIndex;
local ClientConnection connection;
connectionIndex = FindConnection(nicePlayer);
if(connectionIndex < 0)
return false;
connection = connections[connectionIndex];
dataToCheck = NiceServerData(GetData(dataID));
if(dataToCheck == none)
return false;
switch(dataToCheck.priority){
case NSP_REALTIME:
return false;
case NSP_HIGH:
return HasPendingChangesInQueue(dataID,
connection.highPriorityQueue);
default:
return HasPendingChangesInQueue(dataID,
connection.lowPriorityQueue);
}
return false;
}
protected function bool HasPendingChangesInQueue( string dataID,
RequestQueue queue){
local int i;
for(i = queue.newIndex;i < queue.requests.length;i ++)
if(queue.requests[i].dataID == dataID)
return true;
return false;
}
protected function bool DoesQueueContainRequest
(
RequestQueue queue,
ReplicationRequest request
){
local int i;
for(i = queue.newIndex;i < queue.requests.length;i ++)
if(queue.requests[i] == request)
return true;
return false;
}
// Replicates most pressing request for given connection.
// Returns 'true' if we were able to replicate something.
protected function bool ReplicateTopConnectionRequest
(
NicePlayerController clientRef
){
local int queueLength;
local int connectionIndex;
local ClientConnection connectionCopy;
connectionIndex = FindConnection(clientRef);
if(connectionIndex < 0)
return false;
connectionCopy = connections[connectionIndex];
// Try high priority queue
queueLength = connectionCopy.highPriorityQueue.requests.length -
connectionCopy.highPriorityQueue.newIndex;
if(queueLength > 0){
ReplicateTopQueueRequest(clientRef, connectionCopy.highPriorityQueue);
connections[connectionIndex] = connectionCopy;
return true;
}
// Then, if high-priority one was empty, try low priority queue
queueLength = connectionCopy.lowPriorityQueue.requests.length -
connectionCopy.lowPriorityQueue.newIndex;
if(queueLength > 0){
ReplicateTopQueueRequest(clientRef, connectionCopy.lowPriorityQueue);
connections[connectionIndex] = connectionCopy;
return true;
}
return false;
}
// Replicates top request of given queue and removes former from the latter.
// - Requires queue to be non-empty.
// - Doesn't check if client and queue are related.
protected function ReplicateTopQueueRequest(NicePlayerController clientRef,
out RequestQueue queue){
local ReplicationRequest request;
local NiceServerData dataToReplicate;
local bool replicationFinished;
request = queue.requests[queue.newIndex];
dataToReplicate = NiceServerData(GetData(request.dataID));
if(dataToReplicate == none)
return;
// Update queue index first, so that 'HasPendingChanges'
// can return an up-to-date result.
queue.newIndex ++;
replicationFinished = !HasPendingChanges( dataToReplicate.GetID(),
clientRef);
dataToReplicate.ReplicateVariableToClient( V(request.dataID),
request.variable, clientRef,
replicationFinished);
// Preserve invariant
if(queue.newIndex >= queue.requests.length){
queue.newIndex = 0;
queue.requests.length = 0;
}
}
protected function PushRequestToConnection
( ReplicationRequest request,
NicePlayerController clientRef,
NiceData.EDataPriority priority
){
local int connectionIndex;
local RequestQueue givenQueue;
local ClientConnection connectionCopy;
if(priority == NSP_REALTIME) return;
connectionIndex = FindConnection(clientRef);
if(connectionIndex < 0)
return;
connectionCopy = connections[connectionIndex];
// Use appropriate queue
switch(priority){
case NSP_HIGH:
givenQueue = connectionCopy.highPriorityQueue;
if(!DoesQueueContainRequest(givenQueue, request)){
connectionCopy.highPriorityQueue.
requests[givenQueue.requests.length] = request;
}
break;
case NSP_LOW:
givenQueue = connectionCopy.lowPriorityQueue;
if(!DoesQueueContainRequest(givenQueue, request)){
connectionCopy.lowPriorityQueue.
requests[givenQueue.requests.length] = request;
}
break;
default:
return;
}
connections[connectionIndex] = connectionCopy;
}
// Pushes requests to replicate variable change to all active connections
// #private
function PushRequestIntoQueues( NiceRemoteHack.DataRef dataRef,
string variableName,
NiceData.EDataPriority priority){
local int i;
local NiceServerData data;
local ReplicationRequest request;
data = NiceServerData(GetData(dataRef.ID));
if(data == none)
return;
request.dataID = data.GetID();
request.variable = variableName;
for(i = 0;i < connections.length;i ++)
if(data.IsListener(connections[i].player))
PushRequestToConnection(request, connections[i].player, priority);
}
// Pushes requests necessary to perform initial replication
// of given 'updatedData' to given 'nicePlayer'
// #private
function PushDataIntoQueue( NiceRemoteHack.DataRef dataRef,
NicePlayerController clientRef,
NiceData.EDataPriority priority){
local int i;
local NiceServerData dataToPush;
local ReplicationRequest request;
local array<string> dataVariables;
dataToPush = NiceServerData(GetData(dataRef.ID));
if(dataToPush == none)
return;
request.dataID = dataToPush.GetID();
dataVariables = dataToPush.GetVariableNames();
for(i = 0;i < dataVariables.length;i ++){
request.variable = dataVariables[i];
PushRequestToConnection(request, clientRef, priority);
}
}
function Tick(float delta){
local int i;
local bool didReplicate;
for(i = 0;i < connections.length;i ++){
if(connections[i].replicationCountdown > 0)
connections[i].replicationCountdown -= delta;
if(connections[i].replicationCountdown <= 0.0){
didReplicate = ReplicateTopConnectionRequest(connections[i].player);
if(didReplicate)
connections[i].replicationCountdown = replicationCooldown;
}
}
}
defaultproperties
{
}

View file

@ -0,0 +1,206 @@
//==============================================================================
// NicePack / NiceStorageServerBase
//==============================================================================
// Implements storage methods relevant only to server.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceStorageServerBase extends NiceStorageBase
abstract
config(NicePack);
struct ReplicationRequest{
var string dataID;
var string variable;
};
struct RequestQueue{
// Elements with indices below this one were already replicated
var int newIndex;
var array<ReplicationRequest> requests;
// All changes must preserve following invariants:
// - newIndex >= 0
// - newIndex <= requests.length
};
struct ClientConnection{
var NicePlayerController player;
var float replicationCountdown;
var RequestQueue lowPriorityQueue;
var RequestQueue highPriorityQueue;
};
// List of all the players who are connected to any of our data
var protected array<ClientConnection> connections;
// How much time needs to pass before we send new data;
// Each client has it's own cooldowns;
// Not applicable to 'NSP_REALTIME' priority
// since everything is replicated immediately then.
var config float replicationCooldown;
//==============================================================================
// > Variables related to purging 'none' actors
// After client disconnects - it's reference will only uselessly
// clutter connection or listeners references, -
// that's why we need to do periodic "clean ups".
// Time between purges
var config float cleanupCooldown;
// We clear all lost connections every purge, but only this many data sets
var config int cleanupPassesPerRound;
var protected float cleanupCountDown;
// Next index of the next data to be cleaned
var protected int cleanupNextDataIndex;
function bool CreateData(string ID, NiceData.EDataPriority priority){
local NiceServerData serverData;
if(ID == "") return false;
if(DoesDataExistLocally(ID)) return false;
serverData = NiceServerData(class'NiceServerData'.static.NewData(ID));
if(!StoreData(serverData, priority))
return false;
return true;
}
// Puts given with data in the storage without any synchronization work.
// Can fail if data with the same ID already exists.
function bool StoreData(NiceData data, NiceData.EDataPriority priority){
local string ID;
local NiceServerData serverData;
serverData = NiceServerData(data);
if(serverData == none) return false;
ID = serverData.GetID();
if(DoesDataExistLocally(ID))
return false;
localStorage[localStorage.length] = serverData;
serverData.SetOwnerStorage(V(ID), self);
serverData.SetPriority(V(ID), priority);
events.static.CallDataCreated(ID);
return true;
}
function bool CanGrantWriteRights( NiceServerData data,
NicePlayerController clientRef){
local bool isClientAdmin;
if(data == none) return false;
if(data.GetWriteRightsOwner() != none) return false;
// Admin rights check
isClientAdmin = false;
if(clientRef != none && clientRef.PlayerReplicationInfo != none)
isClientAdmin = clientRef.PlayerReplicationInfo.bAdmin;
if(data.isAdminOnly && !isClientAdmin)
return false;
return true;
}
// #private
function bool OpenWriteAccess(DataRef dataRef, NicePlayerController niceClient){
local NiceServerData data;
if(niceClient == none || niceClient.remoteRI == none) return false;
data = NiceServerData(GetData(dataRef.ID));
if(data == none)
return false;
if(CanGrantWriteRights(data, niceClient)){
data.SetWriteRightsOwner(dataRef, niceClient);
events.static.CallWriteAccessGranted(dataRef.ID, niceClient);
niceClient.remoteRI.ClientOpenWriteRights(dataRef);
return true;
}
events.static.CallWriteAccessRefused( dataRef.ID,
data.GetWriteRightsOwner());
niceClient.remoteRI.ClientRefuseWriteRights(dataRef);
return false;
}
// #private
function bool CloseWriteAccess(DataRef dataRef){
local NiceServerData data;
local NicePlayerController oldOwner;
data = NiceServerData(GetData(dataRef.ID));
if(data == none)
return false;
oldOwner = data.GetWriteRightsOwner();
if(oldOwner == none)
return false;
data.SetWriteRightsOwner(dataRef, none);
events.static.CallWritingAccessRevoked(dataRef.ID, oldOwner);
if(oldOwner.remoteRI != none)
oldOwner.remoteRI.ClientCloseWriteRights(dataRef);
return true;
}
function AddConnection(NicePlayerController clientRef){
local int i;
local int newIndex;
local ClientConnection newConnection;
if(clientRef == none) return;
for(i = 0;i < connections.length;i ++)
if(connections[i].player == clientRef)
return;
newConnection.player = clientRef;
newConnection.lowPriorityQueue.newIndex = 0;
newConnection.highPriorityQueue.newIndex = 0;
newIndex = connections.length;
connections[newIndex] = newConnection;
}
// Returns index for a connection for 'clientRef',
// returns -1 if there's no connection for it.
protected function int FindConnection(NicePlayerController clientRef){
local int i;
// Connection can contain 'none' values due to players disconnecting,
if(clientRef == none)
return -1;
for(i = 0;i < connections.length;i ++)
if(connections[i].player == clientRef)
return i;
return -1;
}
protected function CleanupConnections(){
local int i;
local array<ClientConnection> newConnections;
for(i = 0;i < connections.length;i ++)
if(connections[i].player != none)
newConnections[newConnections.length] = connections[i];
connections = newConnections;
}
// There might be a potentially huge number of data with listeners,
// so we'll clean only a certain amount of the at a time.
protected function DoCleanupListenersRound(int passesAmount){
local NiceServerData serverData;
if(localStorage.length <= 0) return;
if(cleanupNextDataIndex < 0 || cleanupNextDataIndex >= localStorage.length)
cleanupNextDataIndex = 0;
serverData = NiceServerData(localStorage[cleanupNextDataIndex]);
if(serverData != none)
serverData.PurgeNullListeners(V(serverData.GetID()));
cleanupNextDataIndex ++;
DoCleanupListenersRound(passesAmount - 1);
}
function Tick(float delta){
cleanupCountDown -= delta;
if(cleanupCountDown <= 0){
cleanupCountDown = cleanupCooldown;
CleanupConnections();
DoCleanupListenersRound(cleanupPassesPerRound);
}
}
defaultproperties
{
dataClass=class'NiceServerData'
cleanupCooldown=1.0
cleanupPassesPerRound=10
replicationCooldown=0.025
}

View file

@ -0,0 +1,19 @@
//==============================================================================
// NicePack / NiceArchivator
//==============================================================================
// "Compresses" and "decompresses" parts of NicePlain data into
// string for replication.
//==============================================================================
// Class hierarchy: Object > NiceArchivator
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceArchivator extends Object;
defaultproperties
{
}

View file

@ -0,0 +1,34 @@
//==============================================================================
// NicePack / NiceDictionary
//==============================================================================
// Stores pair of variable names and their shorteners for `NiceArchivator`.
//==============================================================================
// Class hierarchy: Object > NiceDictionary
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NiceDictionary extends Object
abstract;
struct Definition
{
var string fullName;
var string shortName;
};
var public const array<Definition> definitions;
defaultproperties
{
definitions(0)=(fullName="Location",shortName="L")
definitions(1)=(fullName="Momentum",shortName="M")
definitions(2)=(fullName="HeadshotLevel",shortName="H")
definitions(3)=(fullName="Damage",shortName="D")
definitions(4)=(fullName="LockonTime",shortName="T")
definitions(5)=(fullName="Spread",shortName="S")
definitions(6)=(fullName="ContiniousFire",shortName="C")
}

View file

@ -0,0 +1,183 @@
//==============================================================================
// NicePack / NicePlainData
//==============================================================================
// Provides functionality for local data storage of named variables for
// following types:
// bool, byte, int, float, string, class<Object>.
//==============================================================================
// 'Nice pack' source
// Do whatever the fuck you want with it
// Author: dkanus
// E-mail: dkanus@gmail.com
//==============================================================================
class NicePlainData extends Object;
struct DataPair{
var string key;
var string value;
};
struct Data{
var array<DataPair> pairs;
};
// Returns index of variable with name 'varName', returns -1 if no entry found
static function int LookupVar(Data mySet, string varName){
local int i;
for(i = 0;i < mySet.pairs.length;i ++)
if(mySet.pairs[i].key ~= varName)
return i;
return -1;
}
static function bool GetBool( Data mySet,
string varName,
optional bool defaultValue){
local int index;
index = LookupVar(mySet, varName);
if(index < 0)
return defaultValue;
else
return bool(mySet.pairs[index].value);
}
static function SetBool(out Data mySet, string varName, bool varValue){
local int index;
local DataPair newPair;
index = LookupVar(mySet, varName);
if(index < 0){
newPair.key = varName;
newPair.value = string(varValue);
mySet.pairs[mySet.pairs.length] = newPair;
}
else
mySet.pairs[index].value = string(varValue);
}
static function byte GetByte( Data mySet,
string varName,
optional byte defaultValue){
local int index;
index = LookupVar(mySet, varName);
if(index < 0)
return defaultValue;
else
return byte(mySet.pairs[index].value);
}
static function SetByte(out Data mySet, string varName, byte varValue){
local int index;
local DataPair newPair;
index = LookupVar(mySet, varName);
if(index < 0){
newPair.key = varName;
newPair.value = string(varValue);
mySet.pairs[mySet.pairs.length] = newPair;
}
else
mySet.pairs[index].value = string(varValue);
}
static function int GetInt( Data mySet,
string varName,
optional int defaultValue){
local int index;
index = LookupVar(mySet, varName);
if(index < 0)
return defaultValue;
else
return int(mySet.pairs[index].value);
}
static function SetInt(out Data mySet, string varName, int varValue){
local int index;
local DataPair newPair;
index = LookupVar(mySet, varName);
if(index < 0){
newPair.key = varName;
newPair.value = string(varValue);
mySet.pairs[mySet.pairs.length] = newPair;
}
else
mySet.pairs[index].value = string(varValue);
}
static function float GetFloat( Data mySet,
string varName,
optional float defaultValue){
local int index;
index = LookupVar(mySet, varName);
if(index < 0)
return defaultValue;
else
return float(mySet.pairs[index].value);
}
static function SetFloat(out Data mySet, string varName, float varValue){
local int index;
local DataPair newPair;
index = LookupVar(mySet, varName);
if(index < 0){
newPair.key = varName;
newPair.value = string(varValue);
mySet.pairs[mySet.pairs.length] = newPair;
}
else
mySet.pairs[index].value = string(varValue);
}
static function string GetString( Data mySet,
string varName,
optional string defaultValue){
local int index;
index = LookupVar(mySet, varName);
if(index < 0)
return defaultValue;
else
return mySet.pairs[index].value;
}
static function SetString(out Data mySet, string varName, string varValue){
local int index;
local DataPair newPair;
index = LookupVar(mySet, varName);
if(index < 0){
newPair.key = varName;
newPair.value = varValue;
mySet.pairs[mySet.pairs.length] = newPair;
}
else
mySet.pairs[index].value = varValue;
}
static function class<Object> GetClass( Data mySet,
string varName,
optional class<Object> defaultValue){
local int index;
local string className;
index = LookupVar(mySet, varName);
if(index < 0)
return defaultValue;
className = mySet.pairs[index].value;
return class<Object>(DynamicLoadObject(className, class'Class'));
}
static function SetClass( out Data mySet,
string varName,
optional class<Object> varValue){
local int index;
local DataPair newPair;
index = LookupVar(mySet, varName);
if(index < 0){
newPair.key = varName;
newPair.value = string(varValue);
mySet.pairs[mySet.pairs.length] = newPair;
}
else
mySet.pairs[index].value = string(varValue);
}
defaultproperties
{
}