mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-03-23 01:42:24 +00:00
- NioTSO client isn't needed because we're using RayLib - Added FreeSO's API server to handle most backend operations
398 lines
14 KiB
C#
Executable file
398 lines
14 KiB
C#
Executable file
using FSO.Common.Serialization;
|
|
using FSO.Files.Formats.tsodata;
|
|
using Mina.Core.Buffer;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
|
|
namespace FSO.Server.Protocol.Voltron.DataService
|
|
{
|
|
/// <summary>
|
|
/// TODO: Rewrite this to have much tighter performance
|
|
/// </summary>
|
|
public class cTSOSerializer
|
|
{
|
|
|
|
//private static Logger LOG = LogManager.GetCurrentClassLogger();
|
|
|
|
public static cTSOSerializer INSTANCE = null;
|
|
public static cTSOSerializer Get(){
|
|
return INSTANCE;
|
|
}
|
|
|
|
|
|
public const uint cTSOValue_bool = 0x696D1183;
|
|
public const uint cTSOValue_uint8 = 0xC976087C;
|
|
public const uint cTSOValue_uint16 = 0xE9760891;
|
|
public const uint cTSOValue_uint32 = 0x696D1189;
|
|
public const uint cTSOValue_uint64 = 0x69D3E3DB;
|
|
public const uint cTSOValue_sint8 = 0xE976088A;
|
|
public const uint cTSOValue_sint16 = 0xE9760897;
|
|
public const uint cTSOValue_sint32 = 0x896D1196;
|
|
public const uint cTSOValue_sint64 = 0x89D3E3EF;
|
|
public const uint cTSOValue_string = 0x896D1688;
|
|
public const uint cTSOValue_property = 0xA96E7E5B;
|
|
|
|
private TSODataDefinition Format;
|
|
private Dictionary<uint, Type> ClassesById = new Dictionary<uint, Type>();
|
|
private Dictionary<Type, uint> IdByClass = new Dictionary<Type, uint>();
|
|
|
|
private Dictionary<uint, Type> cNetMessageParametersById = new Dictionary<uint, Type>();
|
|
|
|
|
|
public cTSOSerializer(TSODataDefinition data){
|
|
INSTANCE = this;
|
|
|
|
this.Format = data;
|
|
|
|
//Scan for classes with decorations
|
|
var assembly = Assembly.GetAssembly(typeof(cTSOSerializer));
|
|
|
|
foreach (Type type in assembly.GetTypes())
|
|
{
|
|
System.Attribute[] attributes = System.Attribute.GetCustomAttributes(type);
|
|
|
|
foreach (Attribute attribute in attributes)
|
|
{
|
|
if (attribute is clsid){
|
|
ClassesById.Add(((clsid)attribute).Value, type);
|
|
IdByClass.Add(type, ((clsid)attribute).Value);
|
|
}else if(attribute is cTSONetMessageParameter)
|
|
{
|
|
var param = (cTSONetMessageParameter)attribute;
|
|
object paramValue = param.Value;
|
|
|
|
/*if(paramValue is DBRequestType){
|
|
paramValue = ((DBRequestType)paramValue).GetRequestID();
|
|
}else if(paramValue is DBResponseType)
|
|
{
|
|
paramValue = ((DBResponseType)paramValue).GetResponseID();
|
|
}*/
|
|
cNetMessageParametersById.Add((uint)paramValue, type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public object Deserialize(uint clsid, IoBuffer buffer)
|
|
{
|
|
if (cNetMessageParametersById.ContainsKey(clsid))
|
|
{
|
|
var instance = (IoBufferDeserializable)Activator.CreateInstance(cNetMessageParametersById[clsid]);
|
|
//instance.Deserialize(buffer);
|
|
return instance;
|
|
}
|
|
else if (ClassesById.ContainsKey(clsid))
|
|
{
|
|
var instance = (IoBufferDeserializable)Activator.CreateInstance(ClassesById[clsid]);
|
|
//instance.Deserialize(buffer);
|
|
return instance;
|
|
}
|
|
else if(clsid == cTSOValue_string)
|
|
{
|
|
return buffer.GetPascalVLCString();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public object Serialize(object obj)
|
|
{
|
|
if(obj is IoBufferSerializable)
|
|
{
|
|
return (IoBufferSerializable)obj;
|
|
}
|
|
return GetValue(obj).Value;
|
|
}
|
|
|
|
public cTSOValue GetValue(object obj){
|
|
var type = obj.GetType();
|
|
if (IdByClass.ContainsKey(type))
|
|
{
|
|
uint clsid = IdByClass[type];
|
|
return new cTSOValue() { Type = clsid, Value = obj };
|
|
}
|
|
|
|
throw new Exception("Unknown class " + type);
|
|
}
|
|
|
|
public DerivedStruct GetDerivedStruct(uint id)
|
|
{
|
|
return Format.DerivedStructs.FirstOrDefault(x => x.ID == id);
|
|
}
|
|
|
|
public DerivedStruct GetDerivedStruct(string name)
|
|
{
|
|
return Format.DerivedStructs.FirstOrDefault(x => x.Name == name);
|
|
}
|
|
|
|
/*public List<DataServiceWrapperPDU> SerializeDerivedUpdate(uint avatarId, string derivedTypeName, uint structId, object instance)
|
|
{
|
|
var type = GetDerivedStruct(derivedTypeName);
|
|
|
|
var fields = SerializeDerived(derivedTypeName, structId, instance);
|
|
var result = new List<DataServiceWrapperPDU>();
|
|
|
|
foreach(var update in fields){
|
|
result.Add(new DataServiceWrapperPDU() {
|
|
SendingAvatarID = avatarId,
|
|
RequestTypeID = 0x3998426C,
|
|
Body = update
|
|
});
|
|
}
|
|
|
|
return result;
|
|
}*/
|
|
|
|
/*
|
|
public List<cTSOTopicUpdateMessage> SerializeDerived(string derivedTypeName, uint structId, object instance)
|
|
{
|
|
return SerializeDerived(GetDerivedStruct(derivedTypeName).ID, structId, instance);
|
|
}
|
|
|
|
public List<cTSOTopicUpdateMessage> SerializeDerived(uint derivedType, uint structId, object instance){
|
|
var result = new List<cTSOTopicUpdateMessage>();
|
|
var type = Format.DerivedStructs.First(x => x.ID == derivedType);
|
|
var parent = Format.Structs.First(x => x.ID == type.Parent);
|
|
|
|
foreach(var field in parent.Fields){
|
|
var mask = type.FieldMasks.FirstOrDefault(x => x.ID == field.ID);
|
|
var action = DerivedStructFieldMaskType.KEEP;
|
|
if (mask != null){
|
|
action = mask.Type;
|
|
}
|
|
|
|
if(action == DerivedStructFieldMaskType.REMOVE){
|
|
continue;
|
|
}
|
|
|
|
object value = GetFieldValue(instance, field.Name);
|
|
if (value == null) { continue; }
|
|
|
|
try {
|
|
var serialized = SerializeField(field, value);
|
|
serialized.StructType = parent.ID;
|
|
serialized.StructId = structId;
|
|
result.Add(serialized);
|
|
}catch(Exception ex)
|
|
{
|
|
LOG.Error(ex);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}*/
|
|
|
|
private object GetFieldValue(object obj, string fieldName)
|
|
{
|
|
var objectField = obj.GetType().GetProperty(fieldName);
|
|
if (objectField == null) { return null; }
|
|
|
|
var value = objectField.GetValue(obj);
|
|
|
|
return value;
|
|
}
|
|
|
|
/*private cTSOTopicUpdateMessage SerializeField(StructField field, object value){
|
|
cTSOTopicUpdateMessage result = new cTSOTopicUpdateMessage();
|
|
result.StructField = field.ID;
|
|
|
|
if(field.Classification == StructFieldClassification.List)
|
|
{
|
|
IoBuffer resultBytes = AbstractVoltronPacket.Allocate(4);
|
|
resultBytes.AutoExpand = true;
|
|
var serializedValues = new List<cTSOValue>();
|
|
System.Collections.ICollection list = (System.Collections.ICollection)value;
|
|
|
|
foreach (var item in list){
|
|
serializedValues.Add(SerializeValue(field.TypeID, item));
|
|
}
|
|
|
|
var itemType = serializedValues.First().Type;
|
|
var vectorType = GetVectorClsId(itemType);
|
|
|
|
resultBytes.PutUInt32((uint)serializedValues.Count);
|
|
|
|
foreach (var serializedValue in serializedValues){
|
|
//resultBytes.PutSerializable(serializedValue.Value);
|
|
}
|
|
|
|
resultBytes.Flip();
|
|
|
|
/*result.cTSOValue = new cTSOValue {
|
|
Type = 0xA97384A3,//field.TypeID,
|
|
Value = resultBytes
|
|
};
|
|
|
|
}else if(field.Classification == StructFieldClassification.SingleField)
|
|
{
|
|
var serializedValue = SerializeValue(field.TypeID, value);
|
|
//result.cTSOValue = serializedValue;
|
|
}
|
|
|
|
return result;
|
|
}*/
|
|
|
|
private uint GetVectorClsId(uint clsid)
|
|
{
|
|
switch (clsid)
|
|
{
|
|
//cTSOValue < unsigned long>
|
|
case 0x696D1189:
|
|
//cTSOValueVector < unsigned long>
|
|
return 0x89738496;
|
|
//cTSOValue < long >
|
|
case 0x896D1196:
|
|
//cTSOValueVector < long >
|
|
return 0x8973849A;
|
|
//cTSOValue < unsigned short>
|
|
case 0xE9760891:
|
|
//cTSOValueVector < unsigned short>
|
|
return 0x097608B3;
|
|
//cTSOValue < short >
|
|
case 0xE9760897:
|
|
//cTSOValueVector < short >
|
|
return 0x097608B6;
|
|
//cTSOValue<class cRZAutoRefCount<class cITSOProperty> >
|
|
case 0xA96E7E5B:
|
|
//cTSOValueVector<class cRZAutoRefCount<class cITSOProperty> >
|
|
return 0xA97384A3;
|
|
//cTSOValue <class cRZAutoRefCount<class cIGZString> >
|
|
default:
|
|
throw new Exception("Cannot map clsid to vector clsid, " + clsid);
|
|
}
|
|
}
|
|
|
|
private cTSOValue SerializeValue(uint type, object value)
|
|
{
|
|
var result = new cTSOValue();
|
|
IoBuffer buffer = null;
|
|
|
|
switch (type)
|
|
{
|
|
case 0x48BC841E:
|
|
if (!(value is sbyte) && !(value is Enum))
|
|
{
|
|
return null;
|
|
}
|
|
result.Type = cTSOValue_sint8;
|
|
result.Value = new sbyte[] { Convert.ToSByte(value) };
|
|
break;
|
|
|
|
case 0x74336731:
|
|
if (!(value is ushort))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
buffer = AbstractVoltronPacket.Allocate(2);
|
|
buffer.PutUInt16((ushort)value);
|
|
buffer.Flip();
|
|
|
|
result.Type = cTSOValue_uint16;
|
|
result.Value = buffer;
|
|
break;
|
|
|
|
case 0xF192ECA6:
|
|
if (!(value is short))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
buffer = AbstractVoltronPacket.Allocate(2);
|
|
buffer.PutInt16((short)value);
|
|
buffer.Flip();
|
|
|
|
result.Type = cTSOValue_sint16;
|
|
result.Value = buffer;
|
|
break;
|
|
|
|
case 0xE0463A2F:
|
|
if (!(value is uint))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
buffer = AbstractVoltronPacket.Allocate(4);
|
|
buffer.PutUInt32((uint)value);
|
|
buffer.Flip();
|
|
|
|
result.Type = cTSOValue_uint32;
|
|
result.Value = buffer;
|
|
break;
|
|
|
|
case 0xA0587098:
|
|
if (!(value is int))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
buffer = AbstractVoltronPacket.Allocate(4);
|
|
buffer.PutInt32((int)value);
|
|
buffer.Flip();
|
|
|
|
result.Type = cTSOValue_sint32;
|
|
result.Value = buffer;
|
|
break;
|
|
|
|
case 0x385070C9:
|
|
if (!(value is ulong))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
buffer = AbstractVoltronPacket.Allocate(8);
|
|
buffer.PutUInt64((ulong)value);
|
|
buffer.Flip();
|
|
|
|
result.Type = cTSOValue_uint64;
|
|
result.Value = buffer;
|
|
break;
|
|
|
|
case 0x90D315F7:
|
|
if (!(value is long))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
buffer = AbstractVoltronPacket.Allocate(8);
|
|
buffer.PutInt64((long)value);
|
|
buffer.Flip();
|
|
|
|
result.Type = cTSOValue_sint64;
|
|
result.Value = buffer;
|
|
break;
|
|
|
|
default:
|
|
//It may be a struct
|
|
var _struct = Format.Structs.FirstOrDefault(x => x.ID == type);
|
|
if (_struct != null)
|
|
{
|
|
var body = new cITSOProperty();
|
|
body.StructType = _struct.ID;
|
|
body.StructFields = new List<cITSOField>();
|
|
|
|
foreach(var field in _struct.Fields){
|
|
object fieldValue = GetFieldValue(value, field.Name);
|
|
if (fieldValue == null) { continue; }
|
|
|
|
body.StructFields.Add(new cITSOField {
|
|
ID = field.ID,
|
|
Value = SerializeValue(field.TypeID, fieldValue)
|
|
});
|
|
}
|
|
|
|
result.Type = cTSOValue_property;
|
|
result.Value = body;
|
|
return result;
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|