First commit
This commit is contained in:
commit
5b48414900
263 changed files with 24830 additions and 0 deletions
126
sources/Data/NiceClientData.uc
Normal file
126
sources/Data/NiceClientData.uc
Normal 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
243
sources/Data/NiceData.uc
Normal 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'
|
||||
}
|
||||
16
sources/Data/NiceDataQueue.uc
Normal file
16
sources/Data/NiceDataQueue.uc
Normal 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
|
||||
{
|
||||
}
|
||||
79
sources/Data/NiceRemoteDataAdapter.uc
Normal file
79
sources/Data/NiceRemoteDataAdapter.uc
Normal 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
|
||||
{
|
||||
}
|
||||
149
sources/Data/NiceRemoteDataEvents.uc
Normal file
149
sources/Data/NiceRemoteDataEvents.uc
Normal 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);
|
||||
}
|
||||
47
sources/Data/NiceRemoteHack.uc
Normal file
47
sources/Data/NiceRemoteHack.uc
Normal 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;
|
||||
}
|
||||
258
sources/Data/NiceRepInfoRemoteData.uc
Normal file
258
sources/Data/NiceRepInfoRemoteData.uc
Normal 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
|
||||
{
|
||||
}
|
||||
262
sources/Data/NiceServerData.uc
Normal file
262
sources/Data/NiceServerData.uc
Normal 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
|
||||
}
|
||||
51
sources/Data/NiceStorageBase.uc
Normal file
51
sources/Data/NiceStorageBase.uc
Normal 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'
|
||||
}
|
||||
191
sources/Data/NiceStorageClient.uc
Normal file
191
sources/Data/NiceStorageClient.uc
Normal 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'
|
||||
}
|
||||
224
sources/Data/NiceStorageServer.uc
Normal file
224
sources/Data/NiceStorageServer.uc
Normal 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
|
||||
{
|
||||
}
|
||||
206
sources/Data/NiceStorageServerBase.uc
Normal file
206
sources/Data/NiceStorageServerBase.uc
Normal 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
|
||||
}
|
||||
19
sources/Data/Plain/NiceArchivator.uc
Normal file
19
sources/Data/Plain/NiceArchivator.uc
Normal 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
|
||||
{
|
||||
}
|
||||
34
sources/Data/Plain/NiceDictionary.uc
Normal file
34
sources/Data/Plain/NiceDictionary.uc
Normal 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")
|
||||
}
|
||||
183
sources/Data/Plain/NicePlainData.uc
Normal file
183
sources/Data/Plain/NicePlainData.uc
Normal 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
|
||||
{
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue