Working code
commit
6490c98849
|
|
@ -0,0 +1 @@
|
||||||
|
.DS_Store
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c919269e61d904cf0964b56eb9b1b6ab
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
# GURU Dof kcp Client
|
||||||
|
|
||||||
|
### VERSION 0.0.1
|
||||||
|
|
||||||
|
## 插件介绍
|
||||||
|
|
||||||
|
基于upm_guru_kcp库,服务与dof项目的客户端类封装
|
||||||
|
|
||||||
|
|
||||||
|
## 安装和接入
|
||||||
|
|
||||||
|
### 插件引入
|
||||||
|
|
||||||
|
- 本插件需要使用公司内部的Gitlab加载对应的repo, 详见 [Castbox内部git服务器Gitea使用指南](https://docs.google.com/document/d/1DiGPDD5Teu2QcYaBhqAcsqMbb3DJ5sdnTQMuvVzJSUk/edit#heading=h.w9tkkzwwyjxf)
|
||||||
|
- 根据文档部署好本机配置后, 请在Unity内部配置如下参数
|
||||||
|
- TBD
|
||||||
|
- TBD
|
||||||
|
- TBD
|
||||||
|
|
||||||
|
|
||||||
|
## 逻辑实现
|
||||||
|
|
||||||
|
- TBD
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 问题答疑
|
||||||
|
|
||||||
|
- Q
|
||||||
|
> A
|
||||||
|
|
||||||
|
- Q
|
||||||
|
> A
|
||||||
|
|
||||||
|
- Q
|
||||||
|
> A
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 177739947c6344e95a62c8671cf2e96c
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 23b66401566ec4490b51f7d2375e1228
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,509 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Guru
|
||||||
|
{
|
||||||
|
|
||||||
|
public class GameEventMgr
|
||||||
|
{
|
||||||
|
private static GameEventMgr _instance;
|
||||||
|
public static GameEventMgr Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_instance == null)
|
||||||
|
{
|
||||||
|
_instance = new GameEventMgr();
|
||||||
|
}
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Init(bool is_debug_mode = false)
|
||||||
|
{
|
||||||
|
if (_instance == null)
|
||||||
|
{
|
||||||
|
_instance = new GameEventMgr();
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDebugMode = is_debug_mode;
|
||||||
|
|
||||||
|
if(_isDebugMode)
|
||||||
|
{
|
||||||
|
Debug.Log($"[GameEventMgr]Init: Running in debug mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool _isDebugMode = false;
|
||||||
|
|
||||||
|
struct DelegateGameObjectBonding
|
||||||
|
{
|
||||||
|
public GameObject gameObject;
|
||||||
|
public Delegate listener;
|
||||||
|
|
||||||
|
public DelegateGameObjectBonding(GameObject game_object, Delegate del)
|
||||||
|
{
|
||||||
|
gameObject = game_object;
|
||||||
|
listener = del;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Queue<List<Delegate>> _listenerListQueue = new ();
|
||||||
|
private readonly Queue<List<DelegateGameObjectBonding>> _listenerBondingListQueue = new ();
|
||||||
|
private readonly Dictionary<int, List<Delegate>> _eventTable = new ();
|
||||||
|
private readonly Dictionary<int, List<DelegateGameObjectBonding>> _eventObjTable = new ();
|
||||||
|
private readonly Dictionary<int, List<Delegate>> _pendingAddTable = new ();
|
||||||
|
private readonly Dictionary<int, List<DelegateGameObjectBonding>> _pendingObjAddTable = new ();
|
||||||
|
private readonly Dictionary<int, List<Delegate>> _pendingRemoveTable = new ();
|
||||||
|
private readonly Dictionary<int, List<DelegateGameObjectBonding>> _pendingObjRemoveTable = new ();
|
||||||
|
|
||||||
|
// Using raising event list to prevent infinite loop call or changing delegate list
|
||||||
|
private readonly List<int> _raisingEventIds = new ();
|
||||||
|
|
||||||
|
private GameEventMgr() { }
|
||||||
|
|
||||||
|
public void AddListener(int event_id, Action new_listener, GameObject life_cycle_obj = null)
|
||||||
|
{
|
||||||
|
DoAddListener(event_id, new_listener, life_cycle_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddListener<T>(int event_id, Action<T> new_listener, GameObject life_cycle_obj = null)
|
||||||
|
{
|
||||||
|
DoAddListener(event_id, new_listener, life_cycle_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddListener<T1, T2>(int event_id, Action<T1, T2> new_listener, GameObject life_cycle_obj = null)
|
||||||
|
{
|
||||||
|
DoAddListener(event_id, new_listener, life_cycle_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddListener<T1, T2, T3>(int event_id, Action<T1, T2, T3> new_listener, GameObject life_cycle_obj = null)
|
||||||
|
{
|
||||||
|
DoAddListener(event_id, new_listener, life_cycle_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveListener(int event_id, Action listener, GameObject life_cycle_obj = null)
|
||||||
|
{
|
||||||
|
DoRemoveListener(event_id, listener, life_cycle_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveListener<T>(int event_id, Action<T> listener, GameObject life_cycle_obj = null)
|
||||||
|
{
|
||||||
|
DoRemoveListener(event_id, listener, life_cycle_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveListener<T1, T2>(int event_id, Action<T1, T2> listener, GameObject life_cycle_obj = null)
|
||||||
|
{
|
||||||
|
DoRemoveListener(event_id, listener, life_cycle_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveListener<T1, T2, T3>(int event_id, Action<T1, T2, T3> listener, GameObject life_cycle_obj = null)
|
||||||
|
{
|
||||||
|
DoRemoveListener(event_id, listener, life_cycle_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Raise(int event_id)
|
||||||
|
{
|
||||||
|
DoRaise(event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Raise<T>(int event_id, T arg1)
|
||||||
|
{
|
||||||
|
DoRaise(event_id, arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Raise<T1, T2>(int event_id, T1 arg1, T2 arg2)
|
||||||
|
{
|
||||||
|
DoRaise(event_id, arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Raise<T1, T2, T3>(int event_id, T1 arg1, T2 arg2, T3 arg3)
|
||||||
|
{
|
||||||
|
DoRaise(event_id, arg1, arg2, arg3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearNullGameObjectListeners()
|
||||||
|
{
|
||||||
|
var etor = _eventObjTable.GetEnumerator();
|
||||||
|
|
||||||
|
while (etor.MoveNext())
|
||||||
|
{
|
||||||
|
var listeners = etor.Current.Value;
|
||||||
|
|
||||||
|
for (int i = 0; i < listeners.Count; i++)
|
||||||
|
{
|
||||||
|
if(listeners[i].gameObject == null)
|
||||||
|
{
|
||||||
|
listeners.RemoveAt(i--);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Delegate> SpawnDelegateList()
|
||||||
|
{
|
||||||
|
if(_listenerListQueue.Count > 0)
|
||||||
|
{
|
||||||
|
return _listenerListQueue.Dequeue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new List<Delegate>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DespawnDelegateList(List<Delegate> list)
|
||||||
|
{
|
||||||
|
if (list == null) return;
|
||||||
|
|
||||||
|
list.Clear();
|
||||||
|
_listenerListQueue.Enqueue(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<DelegateGameObjectBonding> SpawnDelegateBondingList()
|
||||||
|
{
|
||||||
|
if (_listenerBondingListQueue.Count > 0)
|
||||||
|
{
|
||||||
|
return _listenerBondingListQueue.Dequeue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new List<DelegateGameObjectBonding>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DespawnDelegateBondingList(List<DelegateGameObjectBonding> list)
|
||||||
|
{
|
||||||
|
if (list == null) return;
|
||||||
|
|
||||||
|
list.Clear();
|
||||||
|
_listenerBondingListQueue.Enqueue(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DoAddListener(int event_id, Delegate new_listener, GameObject life_cycle_obj = null)
|
||||||
|
{
|
||||||
|
if (new_listener == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[GameEventMgr]DoAddListener: Can't add empty listener for event {event_id}!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
// If event is raising, add it into pending list
|
||||||
|
if (_raisingEventIds.Contains(event_id))
|
||||||
|
{
|
||||||
|
if (life_cycle_obj != null)
|
||||||
|
{
|
||||||
|
if (!_pendingObjAddTable.TryGetValue(event_id, out var pending_listeners))
|
||||||
|
{
|
||||||
|
pending_listeners = SpawnDelegateBondingList();
|
||||||
|
_pendingObjAddTable[event_id] = pending_listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
pending_listeners.Add(new DelegateGameObjectBonding(life_cycle_obj, new_listener));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!_pendingAddTable.TryGetValue(event_id, out var pending_listeners))
|
||||||
|
{
|
||||||
|
pending_listeners = SpawnDelegateList();
|
||||||
|
_pendingAddTable[event_id] = pending_listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
pending_listeners.Add(new_listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (life_cycle_obj != null)
|
||||||
|
{
|
||||||
|
if (!_eventObjTable.TryGetValue(event_id, out var listener_bondings))
|
||||||
|
{
|
||||||
|
listener_bondings = SpawnDelegateBondingList();
|
||||||
|
_eventObjTable[event_id] = listener_bondings;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listener_bondings.Count == 0 || listener_bondings[0].listener.GetType() == new_listener.GetType())
|
||||||
|
{
|
||||||
|
var bonding = new DelegateGameObjectBonding(life_cycle_obj, new_listener);
|
||||||
|
|
||||||
|
if (_isDebugMode && listener_bondings.Contains(bonding))
|
||||||
|
{
|
||||||
|
Debug.LogError($"[GameEventMgr]DoAddListener: Already in the list, will not add it again [{new_listener.Target}.{new_listener.Method?.Name}]");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
listener_bondings.Add(bonding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (listener_bondings[0].listener.GetType() != new_listener.GetType())
|
||||||
|
{
|
||||||
|
Debug.LogError($"[GameEventMgr]DoAddListener: Attempting to add listener with inconsistent signature for event {event_id}. Current listeners type({listener_bondings[0].listener.GetType().Name}) != added type({new_listener.GetType().Name})");
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!_eventTable.TryGetValue(event_id, out var listeners))
|
||||||
|
{
|
||||||
|
listeners = SpawnDelegateList();
|
||||||
|
_eventTable[event_id] = listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listeners.Count == 0 || listeners[0].GetType() == new_listener.GetType())
|
||||||
|
{
|
||||||
|
if (_isDebugMode && listeners.Contains(new_listener))
|
||||||
|
{
|
||||||
|
Debug.LogError($"[GameEventMgr]DoAddListener: Already in the list, will not add it again [{new_listener.Target}.{new_listener.Method?.Name}]");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
listeners.Add(new_listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (listeners[0].GetType() != new_listener.GetType())
|
||||||
|
{
|
||||||
|
Debug.LogError($"[GameEventMgr]DoAddListener: Attempting to add listener with inconsistent signature for event {event_id}. Current listeners type({listeners[0].GetType().Name}) != added type({new_listener.GetType().Name})");
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DoRemoveListener(int event_id, Delegate listener, GameObject life_cycle_obj = null)
|
||||||
|
{
|
||||||
|
if (listener == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[GameEventMgr]DoRemoveListener: Can't remove empty listener for event {event_id}!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
// If event is raising, add it into pending list
|
||||||
|
if (_raisingEventIds.Contains(event_id))
|
||||||
|
{
|
||||||
|
if (life_cycle_obj != null)
|
||||||
|
{
|
||||||
|
if (!_pendingObjRemoveTable.TryGetValue(event_id, out var pending_listeners))
|
||||||
|
{
|
||||||
|
pending_listeners = SpawnDelegateBondingList();
|
||||||
|
_pendingObjRemoveTable[event_id] = pending_listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
pending_listeners.Add(new DelegateGameObjectBonding(life_cycle_obj, listener));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!_pendingRemoveTable.TryGetValue(event_id, out var pending_listeners))
|
||||||
|
{
|
||||||
|
pending_listeners = SpawnDelegateList();
|
||||||
|
_pendingRemoveTable[event_id] = pending_listeners;
|
||||||
|
}
|
||||||
|
|
||||||
|
pending_listeners.Add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (life_cycle_obj != null)
|
||||||
|
{
|
||||||
|
if (_eventObjTable.TryGetValue(event_id, out var listeners))
|
||||||
|
{
|
||||||
|
if (listeners.Count > 0)
|
||||||
|
{
|
||||||
|
listeners.Remove(new DelegateGameObjectBonding(life_cycle_obj, listener));
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listeners.Count == 0)
|
||||||
|
{
|
||||||
|
DespawnDelegateBondingList(listeners);
|
||||||
|
_eventObjTable.Remove(event_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_eventTable.TryGetValue(event_id, out var listeners))
|
||||||
|
{
|
||||||
|
if (listeners.Count > 0)
|
||||||
|
{
|
||||||
|
listeners.Remove(listener);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listeners.Count == 0)
|
||||||
|
{
|
||||||
|
DespawnDelegateList(listeners);
|
||||||
|
_eventTable.Remove(event_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogException(Exception ex, string event_tag, string method_name)
|
||||||
|
{
|
||||||
|
string msg = ex.Message;
|
||||||
|
string stack_trace = ex.StackTrace;
|
||||||
|
|
||||||
|
if (ex.InnerException != null)
|
||||||
|
{
|
||||||
|
msg = ex.InnerException.Message;
|
||||||
|
stack_trace = ex.InnerException.StackTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.LogError($"[GameEventMgr]{method_name}: Event({event_tag}) {msg}\nStack Trace: {stack_trace}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DoRaise(int event_id, params object[] args)
|
||||||
|
{
|
||||||
|
if (_raisingEventIds.Contains(event_id))
|
||||||
|
{
|
||||||
|
Debug.LogError($"[GameEventMgr]DoRaise: Can't raise event({event_id}) again inside the same event raising process!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
|
_raisingEventIds.Add(event_id);
|
||||||
|
|
||||||
|
if (_eventObjTable.TryGetValue(event_id, out var bond_listeners))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < bond_listeners.Count; i++)
|
||||||
|
{
|
||||||
|
var bond_listener = bond_listeners[i];
|
||||||
|
|
||||||
|
if (bond_listener.gameObject != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
bond_listener.listener.Method.Invoke(bond_listener.listener.Target, args);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
bond_listeners.RemoveAt(i--);
|
||||||
|
LogException(ex, $"{event_id}_Obj[{bond_listener.gameObject.name}]", "DoRaise");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bond_listeners.RemoveAt(i--);
|
||||||
|
Debug.Log($"[GameEventMgr]DoRaise: Remove null GameObject listener ({event_id})!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_eventTable.TryGetValue(event_id, out var listeners))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < listeners.Count; i++)
|
||||||
|
{
|
||||||
|
var listener = listeners[i];
|
||||||
|
|
||||||
|
if(listener != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
listener.Method.Invoke(listener.Target, args);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
listeners.RemoveAt(i--);
|
||||||
|
LogException(ex, event_id.ToString(), "DoRaise");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_raisingEventIds.Remove(event_id);
|
||||||
|
|
||||||
|
if (_pendingObjAddTable.TryGetValue(event_id, out var add_bond_listeners))
|
||||||
|
{
|
||||||
|
if (add_bond_listeners != null && add_bond_listeners.Count > 0)
|
||||||
|
{
|
||||||
|
bond_listeners.AddRange(add_bond_listeners);
|
||||||
|
DespawnDelegateBondingList(add_bond_listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingObjAddTable.Remove(event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pendingObjRemoveTable.TryGetValue(event_id, out var remove_bond_listeners))
|
||||||
|
{
|
||||||
|
if (remove_bond_listeners != null && remove_bond_listeners.Count > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < remove_bond_listeners.Count; i++)
|
||||||
|
{
|
||||||
|
bond_listeners.Remove(remove_bond_listeners[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
DespawnDelegateBondingList(remove_bond_listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingObjRemoveTable.Remove(event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bond_listeners?.Count == 0)
|
||||||
|
{
|
||||||
|
DespawnDelegateBondingList(bond_listeners);
|
||||||
|
_eventObjTable.Remove(event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pendingAddTable.TryGetValue(event_id, out var add_listeners))
|
||||||
|
{
|
||||||
|
if (add_listeners != null && add_listeners.Count > 0)
|
||||||
|
{
|
||||||
|
listeners.AddRange(add_listeners);
|
||||||
|
DespawnDelegateList(add_listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingAddTable.Remove(event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_pendingRemoveTable.TryGetValue(event_id, out var remove_listeners))
|
||||||
|
{
|
||||||
|
if (remove_listeners != null && remove_listeners.Count > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < remove_listeners.Count; i++)
|
||||||
|
{
|
||||||
|
listeners.Remove(remove_listeners[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
DespawnDelegateList(remove_listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingRemoveTable.Remove(event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listeners?.Count == 0)
|
||||||
|
{
|
||||||
|
DespawnDelegateList(listeners);
|
||||||
|
_eventTable.Remove(event_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_eventTable.Clear();
|
||||||
|
_eventObjTable.Clear();
|
||||||
|
_pendingAddTable.Clear();
|
||||||
|
_pendingObjAddTable.Clear();
|
||||||
|
_pendingRemoveTable.Clear();
|
||||||
|
_pendingObjRemoveTable.Clear();
|
||||||
|
_raisingEventIds.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8912da6e3d9994390b3795e3e9b065d9
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,168 @@
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.IO;
|
||||||
|
using dotNetty_kcp;
|
||||||
|
using DotNetty.Buffers;
|
||||||
|
using UnityEngine;
|
||||||
|
using Guru;
|
||||||
|
using Dof;
|
||||||
|
|
||||||
|
namespace DofLibrary
|
||||||
|
{
|
||||||
|
|
||||||
|
public class GameKcpClient : KcpListener
|
||||||
|
{
|
||||||
|
public Action OnConnected;
|
||||||
|
|
||||||
|
private KcpClient _kcpClient;
|
||||||
|
public IMessageSender sender { get; private set; } = new MessageSender();
|
||||||
|
private IMessageReceiver _receiver;
|
||||||
|
private bool _running;
|
||||||
|
|
||||||
|
public GameKcpClient(DotNetty.Unity.Level level = DotNetty.Unity.Level.ALL)
|
||||||
|
{
|
||||||
|
// 指定DotNetty日志的显示级别
|
||||||
|
DotNetty.Unity.UnityLoggerFactory.Default.Level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前 Client 是否处于连接状态
|
||||||
|
/// </summary>
|
||||||
|
public bool Running => _running;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建一个到 Dof GameServer 的连接
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverAddress">Dof GameServer 的地址,形如 xxx.xxx.xxx.xxx</param>
|
||||||
|
/// <param name="port">房间ID</param>
|
||||||
|
/// <param name="receiver">事件回调接口</param>
|
||||||
|
public async void Connect(string serverAddress, int port, IMessageReceiver receiver)
|
||||||
|
{
|
||||||
|
_receiver = receiver;
|
||||||
|
_receiver.MessageSender = sender;
|
||||||
|
_running = true;
|
||||||
|
|
||||||
|
var channelConfig = new ChannelConfig();
|
||||||
|
channelConfig.initNodelay(true, 40, 2, true);
|
||||||
|
channelConfig.Sndwnd = 512;
|
||||||
|
channelConfig.Rcvwnd = 512;
|
||||||
|
channelConfig.Mtu = 512;
|
||||||
|
channelConfig.FecDataShardCount = 10;
|
||||||
|
channelConfig.FecParityShardCount = 3;
|
||||||
|
channelConfig.AckNoDelay = true;
|
||||||
|
channelConfig.Crc32Check = false;
|
||||||
|
channelConfig.UseConvChannel = true;
|
||||||
|
//channelConfig.Conv = UnityEngine.Random.Range(1, int.MaxValue);
|
||||||
|
channelConfig.TimeoutMillis = 10000;
|
||||||
|
|
||||||
|
_kcpClient = new KcpClient();
|
||||||
|
_kcpClient.init(channelConfig);
|
||||||
|
|
||||||
|
var remoteAddress = new IPEndPoint(IPAddress.Parse(serverAddress), port);
|
||||||
|
var task = _kcpClient.BindLocal();
|
||||||
|
await task;
|
||||||
|
var channel = task.Result;
|
||||||
|
_kcpClient.connect(channel, remoteAddress, channelConfig, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessMessage(ServerMessage serverMessage)
|
||||||
|
{
|
||||||
|
if (serverMessage.PlayerEntered != null)
|
||||||
|
{
|
||||||
|
var cid = serverMessage.PlayerEntered.Cid;
|
||||||
|
sender.Cid = cid;
|
||||||
|
_receiver.OnPlayerEntered(cid);
|
||||||
|
}
|
||||||
|
else if(serverMessage.GameStart != null)
|
||||||
|
{
|
||||||
|
_receiver.OnGameStart(serverMessage.GameStart);
|
||||||
|
}
|
||||||
|
else if (serverMessage.LevelStart != null)
|
||||||
|
{
|
||||||
|
_receiver.OnLevelStart(serverMessage.LevelStart);
|
||||||
|
}
|
||||||
|
else if (serverMessage.PointFound != null)
|
||||||
|
{
|
||||||
|
_receiver.OnPointFound(serverMessage.PointFound);
|
||||||
|
}
|
||||||
|
else if (serverMessage.GameFinish != null)
|
||||||
|
{
|
||||||
|
_receiver.OnGameFinish(serverMessage.GameFinish);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// kcp 连接后的回调函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ukcp">kcp连接</param>
|
||||||
|
public void onConnected(Ukcp ukcp)
|
||||||
|
{
|
||||||
|
Debug.Log($"[GameKcpClient]onConnected: conv = {ukcp.getConv()}");
|
||||||
|
sender.Client = ukcp;
|
||||||
|
|
||||||
|
if (OnConnected != null)
|
||||||
|
{
|
||||||
|
Loom.QueueOnMainThread(() =>
|
||||||
|
{
|
||||||
|
OnConnected.Invoke();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// kcp 接收到消息的回调函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="byteBuf">接收到的消息buffer</param>
|
||||||
|
/// <param name="ukcp">kcp连接</param>
|
||||||
|
public void handleReceive(IByteBuffer byteBuf, Ukcp ukcp)
|
||||||
|
{
|
||||||
|
Debug.Log("[GameKcpClient]handleReceive");
|
||||||
|
|
||||||
|
var ms = new MemoryStream(1024 * 1024 * 1);
|
||||||
|
var data_len = byteBuf.ReadableBytes;
|
||||||
|
byteBuf.ReadBytes(ms, data_len);
|
||||||
|
var msg = ProtobufHelper.FromBytes(typeof(ServerMessage), ms.GetBuffer(), 0, data_len);
|
||||||
|
|
||||||
|
if (msg is ServerMessage server_msg)
|
||||||
|
{
|
||||||
|
Loom.QueueOnMainThread(() =>
|
||||||
|
{
|
||||||
|
ProcessMessage(server_msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError("[GameKcpClient]handleReceive: Fail to Deserialize ServerMessage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// kcp 连接或者接收消息出错的回调函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ex">异常</param>
|
||||||
|
/// <param name="ukcp">kcp连接</param>
|
||||||
|
public void handleException(Exception ex, Ukcp ukcp)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[GameKcpClient]handleException: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// kcp 连接关闭时的回调函数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ukcp">kcp连接</param>
|
||||||
|
public void handleClose(Ukcp ukcp)
|
||||||
|
{
|
||||||
|
Debug.Log("[GameKcpClient]: Connection closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关闭 Client,应该在游戏结束时调用
|
||||||
|
/// </summary>
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
_kcpClient?.stop();
|
||||||
|
_running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 71aa1db1be3544524bb58107e80708ca
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "GuruDofLib.Runtime",
|
||||||
|
"rootNamespace": "",
|
||||||
|
"references": [
|
||||||
|
"GUID:832e7ae06a4304a17a11ca2f7b21373d"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e2e9b8b39d6d74d1094c47c89de72e2e
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
using Dof;
|
||||||
|
|
||||||
|
namespace DofLibrary
|
||||||
|
{
|
||||||
|
public interface IMessageReceiver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Set MessageSender for sending messages
|
||||||
|
/// </summary>
|
||||||
|
IMessageSender? MessageSender
|
||||||
|
{
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当玩家进入房间成功时,服务端发送此事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cid">当前房间里的玩家编号,为0|1</param>
|
||||||
|
void OnPlayerEntered(long cid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当房间里已经进入两个玩家时,服务端发送此事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gameStart"></param>
|
||||||
|
void OnGameStart(GameStart gameStart);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当两个玩家都准备好了某个关卡时,服务端发送此事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="levelStart">当前开始的关卡ID</param>
|
||||||
|
void OnLevelStart(LevelStart levelStart);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当另一个玩家找到了当前关卡的某个点时,服务端发送此事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pointFound">另一个玩家找到的点</param>
|
||||||
|
void OnPointFound(PointFound pointFound);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当两个玩家都完成了所有关卡时,服务端发送此事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gameFinish">两个玩家的得分</param>
|
||||||
|
void OnGameFinish(GameFinish gameFinish);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2d2e7c96d274a436895dfb34fdac9d8d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
using dotNetty_kcp;
|
||||||
|
|
||||||
|
namespace DofLibrary
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏消息发送接口
|
||||||
|
/// </summary>
|
||||||
|
public interface IMessageSender
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 设置 kcp 客户端,用于发送消息
|
||||||
|
/// </summary>
|
||||||
|
Ukcp? Client
|
||||||
|
{
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当收到服务端发来的 PlayerEntered 事件时,设置房间中的玩家编号
|
||||||
|
/// </summary>
|
||||||
|
long Cid
|
||||||
|
{
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送 PlayerEnter 进入房间 消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="roomId">房间ID</param>
|
||||||
|
/// <param name="uid">玩家ID</param>
|
||||||
|
/// <param name="nickName">玩家昵称</param>
|
||||||
|
/// <param name="country">玩家国家</param>
|
||||||
|
void PlayerEnter(string roomId, string uid, string nickName, string country);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送 LevelPrepared 关卡准备完毕 消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="levelId">关卡ID</param>
|
||||||
|
void LevelPrepared(string levelId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送 PointFound 找到点位 消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="levelId">当前关卡ID</param>
|
||||||
|
/// <param name="pointId">点位编号</param>
|
||||||
|
void PointFound(string levelId, int pointId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送 LevelEnd 关卡结束 消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="levelId">关卡ID</param>
|
||||||
|
void LevelEnd(string levelId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送 AllLevelEnd 所有关卡结束 消息
|
||||||
|
/// </summary>
|
||||||
|
void AllLevelEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 29452c4084f2f45399bb395cedd91ebd
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,218 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Guru
|
||||||
|
{
|
||||||
|
// Loom lets you easily run code on another thread and have that other thread run code on the main game thread when it needs to.
|
||||||
|
public class Loom : MonoBehaviour
|
||||||
|
{
|
||||||
|
public static int maxThreads = 10;
|
||||||
|
|
||||||
|
private static int numThreads;
|
||||||
|
private static Loom _instance;
|
||||||
|
|
||||||
|
public static Loom Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Awake()
|
||||||
|
{
|
||||||
|
_instance = this;
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool initialized;
|
||||||
|
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
if (!Application.isPlaying) return;
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
var g = new GameObject("Loom");
|
||||||
|
DontDestroyOnLoad(g);
|
||||||
|
_instance = g.AddComponent<Loom>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct DelayedQueueItem
|
||||||
|
{
|
||||||
|
public float time;
|
||||||
|
public Action action;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Action> _actions = new List<Action>();
|
||||||
|
private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>();
|
||||||
|
private List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>();
|
||||||
|
|
||||||
|
// Runs a set of statements on the main thread
|
||||||
|
public static void QueueOnMainThread(Action action)
|
||||||
|
{
|
||||||
|
QueueOnMainThread(action, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runs a set of statements on the main thread (with an optional delay).
|
||||||
|
public static void QueueOnMainThread(Action action, float time)
|
||||||
|
{
|
||||||
|
if (time != 0)
|
||||||
|
{
|
||||||
|
lock (_instance._delayed)
|
||||||
|
{
|
||||||
|
_instance._delayed.Add(new DelayedQueueItem { time = Time.unscaledTime + time, action = action });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lock (_instance._actions)
|
||||||
|
{
|
||||||
|
_instance._actions.Add(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runs a set of statements on another thread
|
||||||
|
public static Thread RunAsync(Action a)
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
|
||||||
|
while (numThreads >= maxThreads)
|
||||||
|
{
|
||||||
|
Thread.Sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Interlocked.Increment(ref numThreads);
|
||||||
|
ThreadPool.QueueUserWorkItem(RunAction, a);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunAction(object action)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
((Action)action)();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Interlocked.Decrement(ref numThreads);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Reset()
|
||||||
|
{
|
||||||
|
if (_instance == null) return;
|
||||||
|
|
||||||
|
lock (_instance._delayed)
|
||||||
|
{
|
||||||
|
_instance._delayed.Clear();
|
||||||
|
_instance._currentDelayed.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_instance._actions)
|
||||||
|
{
|
||||||
|
_instance._actions.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsThreadsMax()
|
||||||
|
{
|
||||||
|
return numThreads >= maxThreads;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDisable()
|
||||||
|
{
|
||||||
|
if (_instance == this)
|
||||||
|
{
|
||||||
|
_instance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Action> _currentActions = new List<Action>();
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (_actions.Count > 0)
|
||||||
|
{
|
||||||
|
lock (_actions)
|
||||||
|
{
|
||||||
|
_currentActions.Clear();
|
||||||
|
_currentActions.AddRange(_actions);
|
||||||
|
_actions.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _currentActions.Count; i++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_currentActions[i].Invoke();
|
||||||
|
}
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
LogException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_delayed.Count > 0)
|
||||||
|
{
|
||||||
|
lock (_delayed)
|
||||||
|
{
|
||||||
|
_currentDelayed.Clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < _delayed.Count; i++)
|
||||||
|
{
|
||||||
|
var d = _delayed[i];
|
||||||
|
|
||||||
|
if (d.time <= Time.unscaledTime)
|
||||||
|
{
|
||||||
|
_currentDelayed.Add(d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _currentDelayed.Count; i++)
|
||||||
|
{
|
||||||
|
var item = _currentDelayed[i];
|
||||||
|
_delayed.Remove(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _currentDelayed.Count; i++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_currentDelayed[i].action.Invoke();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LogException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogException(Exception ex)
|
||||||
|
{
|
||||||
|
string msg = ex.Message;
|
||||||
|
string stack_trace = ex.StackTrace;
|
||||||
|
|
||||||
|
if (ex.InnerException != null)
|
||||||
|
{
|
||||||
|
msg = ex.InnerException.Message;
|
||||||
|
stack_trace = ex.InnerException.StackTrace;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.LogError($"[Loom]InvokeMainThreadAction: {msg}\nStack Trace: {stack_trace}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8d56f3a7c2d1a48c78f742f63a075fb7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
using Dof;
|
||||||
|
using dotNetty_kcp;
|
||||||
|
using DotNetty.Buffers;
|
||||||
|
using UnityEngine;
|
||||||
|
using System.IO;
|
||||||
|
using base_kcp;
|
||||||
|
using Guru;
|
||||||
|
|
||||||
|
namespace DofLibrary
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 游戏消息发送接口实现类
|
||||||
|
/// </summary>
|
||||||
|
internal class MessageSender : IMessageSender
|
||||||
|
{
|
||||||
|
private long _cid;
|
||||||
|
private Ukcp _client;
|
||||||
|
private MemoryStream _localSendMs = new(1024 * 1024 * 1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置 kcp 客户端,用于发送消息
|
||||||
|
/// </summary>
|
||||||
|
public Ukcp Client
|
||||||
|
{
|
||||||
|
set => _client = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当收到服务端发来的 PlayerEntered 事件时,设置房间中的玩家编号
|
||||||
|
/// </summary>
|
||||||
|
public long Cid
|
||||||
|
{
|
||||||
|
set => _cid = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PlayerEnter(string roomId, string uid, string nickName, string country)
|
||||||
|
{
|
||||||
|
var playerEnter = new ClientMessage
|
||||||
|
{
|
||||||
|
PlayerEnter = new PlayerEnter
|
||||||
|
{
|
||||||
|
RoomId = roomId,
|
||||||
|
Uid = uid,
|
||||||
|
NickName = nickName,
|
||||||
|
Country = country
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Send(playerEnter);
|
||||||
|
Debug.Log("PlayerEnter message sent");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LevelPrepared(string levelId)
|
||||||
|
{
|
||||||
|
var message = new ClientMessage
|
||||||
|
{
|
||||||
|
LevelPrepared = new LevelPrepared
|
||||||
|
{
|
||||||
|
Cid = _cid,
|
||||||
|
Level = levelId
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Send(message);
|
||||||
|
Debug.Log("LevelPrepared message sent");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PointFound(string levelId, int pointId)
|
||||||
|
{
|
||||||
|
var message = new ClientMessage
|
||||||
|
{
|
||||||
|
PointFound = new PointFound
|
||||||
|
{
|
||||||
|
Cid = _cid,
|
||||||
|
Level = levelId,
|
||||||
|
PointId = pointId
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Send(message);
|
||||||
|
Debug.Log("PointFound message sent");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LevelEnd(string levelId)
|
||||||
|
{
|
||||||
|
var message = new ClientMessage
|
||||||
|
{
|
||||||
|
LevelEnd = new LevelEnd
|
||||||
|
{
|
||||||
|
Cid = _cid,
|
||||||
|
Level = levelId
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Send(message);
|
||||||
|
Debug.Log("LevelEnd message sent");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AllLevelEnd()
|
||||||
|
{
|
||||||
|
var message = new ClientMessage
|
||||||
|
{
|
||||||
|
AllLevelEnd = new AllLevelEnd
|
||||||
|
{
|
||||||
|
Cid = _cid
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Send(message);
|
||||||
|
Debug.Log("AllLevelEnd message sent");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Send(ClientMessage message)
|
||||||
|
{
|
||||||
|
_localSendMs.SetLength(0);
|
||||||
|
_localSendMs.Position = 0;
|
||||||
|
|
||||||
|
ProtobufHelper.ToStream(message, _localSendMs);
|
||||||
|
|
||||||
|
var dataBuf = Unpooled.DirectBuffer(1024 * 1024 * 1);
|
||||||
|
dataBuf.WriteBytes(_localSendMs.GetBuffer(), 0, (int)_localSendMs.Length);
|
||||||
|
_client.write(dataBuf);
|
||||||
|
dataBuf.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 83018a8afd2ab4af499e831caa714f77
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 92dc6b0ee5121a94bb34348448a983a1
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,263 @@
|
||||||
|
// This file was generated by a tool; you should avoid making direct changes.
|
||||||
|
// Consider using 'partial classes' to extend these types
|
||||||
|
// Input: messages.proto
|
||||||
|
|
||||||
|
#pragma warning disable CS1591, CS0612, CS3021
|
||||||
|
|
||||||
|
namespace Dof
|
||||||
|
{
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class ClientMessage
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"player_enter")]
|
||||||
|
public PlayerEnter PlayerEnter
|
||||||
|
{
|
||||||
|
get { return __pbn__actual.Is(1) ? ((PlayerEnter)__pbn__actual.Object) : default(PlayerEnter); }
|
||||||
|
set { __pbn__actual = new global::ProtoBuf.DiscriminatedUnionObject(1, value); }
|
||||||
|
}
|
||||||
|
public bool ShouldSerializePlayerEnter() => __pbn__actual.Is(1);
|
||||||
|
public void ResetPlayerEnter() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__actual, 1);
|
||||||
|
|
||||||
|
private global::ProtoBuf.DiscriminatedUnionObject __pbn__actual;
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(2, Name = @"level_prepared")]
|
||||||
|
public LevelPrepared LevelPrepared
|
||||||
|
{
|
||||||
|
get { return __pbn__actual.Is(2) ? ((LevelPrepared)__pbn__actual.Object) : default(LevelPrepared); }
|
||||||
|
set { __pbn__actual = new global::ProtoBuf.DiscriminatedUnionObject(2, value); }
|
||||||
|
}
|
||||||
|
public bool ShouldSerializeLevelPrepared() => __pbn__actual.Is(2);
|
||||||
|
public void ResetLevelPrepared() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__actual, 2);
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(3, Name = @"point_found")]
|
||||||
|
public PointFound PointFound
|
||||||
|
{
|
||||||
|
get { return __pbn__actual.Is(3) ? ((PointFound)__pbn__actual.Object) : default(PointFound); }
|
||||||
|
set { __pbn__actual = new global::ProtoBuf.DiscriminatedUnionObject(3, value); }
|
||||||
|
}
|
||||||
|
public bool ShouldSerializePointFound() => __pbn__actual.Is(3);
|
||||||
|
public void ResetPointFound() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__actual, 3);
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(4, Name = @"level_end")]
|
||||||
|
public LevelEnd LevelEnd
|
||||||
|
{
|
||||||
|
get { return __pbn__actual.Is(4) ? ((LevelEnd)__pbn__actual.Object) : default(LevelEnd); }
|
||||||
|
set { __pbn__actual = new global::ProtoBuf.DiscriminatedUnionObject(4, value); }
|
||||||
|
}
|
||||||
|
public bool ShouldSerializeLevelEnd() => __pbn__actual.Is(4);
|
||||||
|
public void ResetLevelEnd() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__actual, 4);
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(5, Name = @"all_level_end")]
|
||||||
|
public AllLevelEnd AllLevelEnd
|
||||||
|
{
|
||||||
|
get { return __pbn__actual.Is(5) ? ((AllLevelEnd)__pbn__actual.Object) : default(AllLevelEnd); }
|
||||||
|
set { __pbn__actual = new global::ProtoBuf.DiscriminatedUnionObject(5, value); }
|
||||||
|
}
|
||||||
|
public bool ShouldSerializeAllLevelEnd() => __pbn__actual.Is(5);
|
||||||
|
public void ResetAllLevelEnd() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__actual, 5);
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(6, Name = @"player_leave")]
|
||||||
|
public PlayerLeave PlayerLeave
|
||||||
|
{
|
||||||
|
get { return __pbn__actual.Is(6) ? ((PlayerLeave)__pbn__actual.Object) : default(PlayerLeave); }
|
||||||
|
set { __pbn__actual = new global::ProtoBuf.DiscriminatedUnionObject(6, value); }
|
||||||
|
}
|
||||||
|
public bool ShouldSerializePlayerLeave() => __pbn__actual.Is(6);
|
||||||
|
public void ResetPlayerLeave() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__actual, 6);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class ServerMessage
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"player_entered")]
|
||||||
|
public PlayerEntered PlayerEntered
|
||||||
|
{
|
||||||
|
get { return __pbn__actual.Is(1) ? ((PlayerEntered)__pbn__actual.Object) : default(PlayerEntered); }
|
||||||
|
set { __pbn__actual = new global::ProtoBuf.DiscriminatedUnionObject(1, value); }
|
||||||
|
}
|
||||||
|
public bool ShouldSerializePlayerEntered() => __pbn__actual.Is(1);
|
||||||
|
public void ResetPlayerEntered() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__actual, 1);
|
||||||
|
|
||||||
|
private global::ProtoBuf.DiscriminatedUnionObject __pbn__actual;
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(2, Name = @"game_start")]
|
||||||
|
public GameStart GameStart
|
||||||
|
{
|
||||||
|
get { return __pbn__actual.Is(2) ? ((GameStart)__pbn__actual.Object) : default(GameStart); }
|
||||||
|
set { __pbn__actual = new global::ProtoBuf.DiscriminatedUnionObject(2, value); }
|
||||||
|
}
|
||||||
|
public bool ShouldSerializeGameStart() => __pbn__actual.Is(2);
|
||||||
|
public void ResetGameStart() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__actual, 2);
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(3, Name = @"level_start")]
|
||||||
|
public LevelStart LevelStart
|
||||||
|
{
|
||||||
|
get { return __pbn__actual.Is(3) ? ((LevelStart)__pbn__actual.Object) : default(LevelStart); }
|
||||||
|
set { __pbn__actual = new global::ProtoBuf.DiscriminatedUnionObject(3, value); }
|
||||||
|
}
|
||||||
|
public bool ShouldSerializeLevelStart() => __pbn__actual.Is(3);
|
||||||
|
public void ResetLevelStart() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__actual, 3);
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(4, Name = @"point_found")]
|
||||||
|
public PointFound PointFound
|
||||||
|
{
|
||||||
|
get { return __pbn__actual.Is(4) ? ((PointFound)__pbn__actual.Object) : default(PointFound); }
|
||||||
|
set { __pbn__actual = new global::ProtoBuf.DiscriminatedUnionObject(4, value); }
|
||||||
|
}
|
||||||
|
public bool ShouldSerializePointFound() => __pbn__actual.Is(4);
|
||||||
|
public void ResetPointFound() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__actual, 4);
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(5, Name = @"game_finish")]
|
||||||
|
public GameFinish GameFinish
|
||||||
|
{
|
||||||
|
get { return __pbn__actual.Is(5) ? ((GameFinish)__pbn__actual.Object) : default(GameFinish); }
|
||||||
|
set { __pbn__actual = new global::ProtoBuf.DiscriminatedUnionObject(5, value); }
|
||||||
|
}
|
||||||
|
public bool ShouldSerializeGameFinish() => __pbn__actual.Is(5);
|
||||||
|
public void ResetGameFinish() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__actual, 5);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class PlayerEnter
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"room_id")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string RoomId { get; set; } = "";
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(2, Name = @"uid")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string Uid { get; set; } = "";
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(3, Name = @"nick_name")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string NickName { get; set; } = "";
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(4, Name = @"country")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string Country { get; set; } = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class LevelResource
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"level_id")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string LevelId { get; set; } = "";
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(2, Name = @"android_generation")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string AndroidGeneration { get; set; } = "";
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(3, Name = @"ios_generation")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string IosGeneration { get; set; } = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class PlayerEntered
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"cid")]
|
||||||
|
public long Cid { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class GameStart
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"level_resource")]
|
||||||
|
public global::System.Collections.Generic.List<LevelResource> LevelResources { get; } = new global::System.Collections.Generic.List<LevelResource>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class LevelPrepared
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"cid")]
|
||||||
|
public long Cid { get; set; }
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(2, Name = @"level")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string Level { get; set; } = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class LevelStart
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"level")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string Level { get; set; } = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class PointFound
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"cid")]
|
||||||
|
public long Cid { get; set; }
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(2, Name = @"level")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string Level { get; set; } = "";
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(3, Name = @"point_id")]
|
||||||
|
public int PointId { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class LevelEnd
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"cid")]
|
||||||
|
public long Cid { get; set; }
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(2, Name = @"level")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string Level { get; set; } = "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class AllLevelEnd
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"cid")]
|
||||||
|
public long Cid { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class GameScore
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"uid")]
|
||||||
|
[global::System.ComponentModel.DefaultValue("")]
|
||||||
|
public string Uid { get; set; } = "";
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoMember(2, Name = @"score")]
|
||||||
|
public int Score { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class GameFinish
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"scores")]
|
||||||
|
public global::System.Collections.Generic.List<GameScore> Scores { get; } = new global::System.Collections.Generic.List<GameScore>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[global::ProtoBuf.ProtoContract()]
|
||||||
|
public partial class PlayerLeave
|
||||||
|
{
|
||||||
|
[global::ProtoBuf.ProtoMember(1, Name = @"cid")]
|
||||||
|
public long Cid { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning restore CS1591, CS0612, CS3021
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d2ca874f5de9f40e0bf69a03d2921c90
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using ProtoBuf.Meta;
|
||||||
|
|
||||||
|
namespace Guru
|
||||||
|
{
|
||||||
|
public interface ISupportInitialize
|
||||||
|
{
|
||||||
|
void BeginInit();
|
||||||
|
void EndInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IDisposable
|
||||||
|
{
|
||||||
|
void Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ProtobufHelper
|
||||||
|
{
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object FromBytes(Type type, byte[] bytes, int index, int count)
|
||||||
|
{
|
||||||
|
using (MemoryStream stream = new MemoryStream(bytes, index, count))
|
||||||
|
{
|
||||||
|
object o = RuntimeTypeModel.Default.Deserialize(stream, null, type);
|
||||||
|
if (o is ISupportInitialize supportInitialize)
|
||||||
|
{
|
||||||
|
supportInitialize.EndInit();
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] ToBytes(object message)
|
||||||
|
{
|
||||||
|
using (MemoryStream stream = new MemoryStream())
|
||||||
|
{
|
||||||
|
ProtoBuf.Serializer.Serialize(stream, message);
|
||||||
|
return stream.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ToStream(object message, MemoryStream stream)
|
||||||
|
{
|
||||||
|
ProtoBuf.Serializer.Serialize(stream, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object FromStream(Type type, MemoryStream stream)
|
||||||
|
{
|
||||||
|
object o = RuntimeTypeModel.Default.Deserialize(stream, null, type);
|
||||||
|
if (o is ISupportInitialize supportInitialize)
|
||||||
|
{
|
||||||
|
supportInitialize.EndInit();
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 87fa43cd32c964f6a9353a6400490b67
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "com.guru.unity.gurudoflib",
|
||||||
|
"displayName": "GuruDofLib",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "基于Guru kcp库实现的客户端接口封装",
|
||||||
|
"unity": "2020.3",
|
||||||
|
"license": "MIT",
|
||||||
|
"category": "Game tool",
|
||||||
|
"dependencies": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 41df6effece9c454c85e2908e1b00150
|
||||||
|
PackageManifestImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Loading…
Reference in New Issue