饭太稀

This commit is contained in:
bob
2025-06-30 10:51:37 +08:00
commit 8e45469c83
753 changed files with 87652 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
#if FANTASY_CONSOLE
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.Serialize;
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8603 // Possible null reference return.
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace Fantasy.Platform.Console
{
public struct OnFantasyInit
{
public Scene Scene;
}
/// <summary>
/// 一般的控制台启动入口,可以适用大部分客户端环境
/// </summary>
public sealed class Entry
{
private static bool _isInit;
private static Thread _updateThread;
public static Scene Scene { get; private set; }
/// <summary>
/// 初始化框架
/// </summary>
/// <param name="assemblies"></param>
public static async FTask Initialize(params System.Reflection.Assembly[] assemblies)
{
if (_isInit)
{
Log.Error("Fantasy has already been initialized and does not need to be initialized again!");
return;
}
// 初始化程序集管理系统
await AssemblySystem.InnerInitialize(assemblies);
// 初始化序列化
SerializerManager.Initialize();
_isInit = true;
Log.Debug("Fantasy Initialize Complete!");
}
/// <summary>
/// 启动框架。
/// 如果您的平台有每帧更新逻辑的方法,请不要调用这个方法。
/// 如果没有实现每帧执行方法平台需要调用这个方法目的是开启一个新的线程来每帧执行Update。
/// 注意因为开启了一个新的线程来处理更新逻辑,所以要注意多线程的问题。
/// </summary>
public static void StartUpdate()
{
_updateThread = new Thread(() =>
{
while (_isInit)
{
ThreadScheduler.Update();
Thread.Sleep(1);
}
})
{
IsBackground = true
};
_updateThread.Start();
}
/// <summary>
/// 在Entry中创建一个Scene如果Scene已经被创建过将先销毁Scene再创建。
/// </summary>
/// <param name="sceneRuntimeMode"></param>
/// <returns></returns>
public static async FTask<Scene> CreateScene(string sceneRuntimeMode = SceneRuntimeMode.MainThread)
{
Scene?.Dispose();
Scene = await Scene.Create(sceneRuntimeMode);
await Scene.EventComponent.PublishAsync(new OnFantasyInit()
{
Scene = Scene
});
return Scene;
}
/// <summary>
/// 如果有的话一定要在每帧执行这个方法
/// </summary>
public void Update()
{
ThreadScheduler.Update();
}
public static void Dispose()
{
AssemblySystem.Dispose();
SerializerManager.Dispose();
Scene?.Dispose();
Scene = null;
_isInit = false;
}
}
}
#endif

View File

@@ -0,0 +1,38 @@
#if FANTASY_CONSOLE
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning disable CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes).
namespace Fantasy
{
public sealed class ThreadSynchronizationContext : SynchronizationContext
{
private Action _actionHandler;
private readonly Queue<Action> _queue = new();
public void Update()
{
while (_queue.TryDequeue(out _actionHandler))
{
try
{
_actionHandler();
}
catch (Exception e)
{
Log.Error(e);
}
}
}
public override void Post(SendOrPostCallback callback, object state)
{
Post(() => callback(state));
}
public void Post(Action action)
{
_queue.Enqueue(action);
}
}
}
#endif

View File

@@ -0,0 +1,88 @@
#if FANTASY_NET
// ReSharper disable InconsistentNaming
using System.Collections.Concurrent;
using System.Runtime.Serialization;
using Fantasy.Helper;
using Newtonsoft.Json;
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace Fantasy.Platform.Net
{
/// <summary>
/// 用于记录服务器物理信息
/// </summary>
public sealed class MachineConfigData
{
/// <summary>
/// 存放所有MachineConfigInfo信息
/// </summary>
public List<MachineConfig> List;
[JsonIgnore]
[IgnoreDataMember]
private readonly ConcurrentDictionary<uint, MachineConfig> _configs = new ConcurrentDictionary<uint, MachineConfig>();
/// <summary>
/// 获得MachineConfig的实例
/// </summary>
public static MachineConfigData Instance { get; private set; }
/// <summary>
/// 初始化MachineConfig
/// </summary>
/// <param name="machineConfigJson"></param>
public static void Initialize(string machineConfigJson)
{
Instance = machineConfigJson.Deserialize<MachineConfigData>();
foreach (var config in Instance.List)
{
Instance._configs.TryAdd(config.Id, config);
}
}
/// <summary>
/// 根据Id获取MachineConfig
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
public MachineConfig Get(uint id)
{
if (_configs.TryGetValue(id, out var machineConfigInfo))
{
return machineConfigInfo;
}
throw new FileNotFoundException($"MachineConfig not find {id} Id");
}
/// <summary>
/// 根据Id获取MachineConfig
/// </summary>
/// <param name="id"></param>
/// <param name="config"></param>
/// <returns></returns>
public bool TryGet(uint id, out MachineConfig config)
{
return _configs.TryGetValue(id, out config);
}
}
/// <summary>
/// 表示一个物理服务器的信息
/// </summary>
public sealed class MachineConfig
{
/// <summary>
/// Id
/// </summary>
public uint Id { get; set; }
/// <summary>
/// 外网IP
/// </summary>
public string OuterIP { get; set; }
/// <summary>
/// 外网绑定IP
/// </summary>
public string OuterBindIP { get; set; }
/// <summary>
/// 内网绑定IP
/// </summary>
public string InnerBindIP { get; set; }
}
}
#endif

View File

@@ -0,0 +1,100 @@
#if FANTASY_NET
using System.Collections.Concurrent;
using System.Runtime.Serialization;
using Fantasy.Helper;
using Newtonsoft.Json;
// ReSharper disable CollectionNeverUpdated.Global
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace Fantasy.Platform.Net
{
/// <summary>
/// 用于管理进程信息
/// </summary>
public sealed class ProcessConfigData
{
/// <summary>
/// 存放所有ProcessConfig信息
/// </summary>
public List<ProcessConfig> List;
[JsonIgnore]
[IgnoreDataMember]
private readonly ConcurrentDictionary<uint, ProcessConfig> _configs = new ConcurrentDictionary<uint, ProcessConfig>();
/// <summary>
/// 获得ProcessConfigData的实例
/// </summary>
public static ProcessConfigData Instance { get; private set; }
/// <summary>
/// 初始化MachineConfig
/// </summary>
/// <param name="processConfigJson"></param>
public static void Initialize(string processConfigJson)
{
Instance = processConfigJson.Deserialize<ProcessConfigData>();
foreach (var config in Instance.List)
{
Instance._configs.TryAdd(config.Id, config);
}
}
/// <summary>
/// 根据Id获取ProcessConfig
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
public ProcessConfig Get(uint id)
{
if (_configs.TryGetValue(id, out var processConfigInfo))
{
return processConfigInfo;
}
throw new FileNotFoundException($"MachineConfig not find {id} Id");
}
/// <summary>
/// 根据Id获取ProcessConfig
/// </summary>
/// <param name="id"></param>
/// <param name="config"></param>
/// <returns></returns>
public bool TryGet(uint id, out ProcessConfig config)
{
return _configs.TryGetValue(id, out config);
}
/// <summary>
/// 按照startupGroup寻找属于startupGroup组的ProcessConfig
/// </summary>
/// <param name="startupGroup">startupGroup</param>
/// <returns></returns>
public IEnumerable<ProcessConfig> ForEachByStartupGroup(uint startupGroup)
{
foreach (var processConfig in List)
{
if (processConfig.StartupGroup == startupGroup)
{
yield return processConfig;
}
}
}
}
/// <summary>
/// 表示一个进程配置信息
/// </summary>
public sealed class ProcessConfig
{
/// <summary>
/// 进程Id
/// </summary>
public uint Id { get; set; }
/// <summary>
/// 机器ID
/// </summary>
public uint MachineId { get; set; }
/// <summary>
/// 启动组
/// </summary>
public uint StartupGroup { get; set; }
}
}
#endif

View File

@@ -0,0 +1,196 @@
#if FANTASY_NET
using System.Collections.Concurrent;
using System.Runtime.Serialization;
using Fantasy.DataStructure.Collection;
using Fantasy.DataStructure.Dictionary;
using Fantasy.Helper;
using Fantasy.IdFactory;
using Newtonsoft.Json;
#pragma warning disable CS8603 // Possible null reference return.
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace Fantasy.Platform.Net
{
/// <summary>
/// 存放所有SceneConfigInfo信息
/// </summary>
public sealed class SceneConfigData
{
/// <summary>
/// 存放所有SceneConfig信息
/// </summary>
public List<SceneConfig> List;
[JsonIgnore]
[IgnoreDataMember]
private readonly ConcurrentDictionary<uint, SceneConfig> _configs = new ConcurrentDictionary<uint, SceneConfig>();
[JsonIgnore]
[IgnoreDataMember]
private readonly OneToManyList<int, SceneConfig> _sceneConfigBySceneType = new OneToManyList<int, SceneConfig>();
[JsonIgnore]
[IgnoreDataMember]
private readonly OneToManyList<uint, SceneConfig> _sceneConfigByProcess = new OneToManyList<uint, SceneConfig>();
[JsonIgnore] [IgnoreDataMember]
private readonly Dictionary<int, Dictionary<int, List<SceneConfig>>> _worldSceneTypes = new Dictionary<int, Dictionary<int, List<SceneConfig>>>();
/// <summary>
/// 获得SceneConfigData的实例
/// </summary>
public static SceneConfigData Instance { get; private set; }
/// <summary>
/// 初始化SceneConfig
/// </summary>
/// <param name="sceneConfigJson"></param>
public static void Initialize(string sceneConfigJson)
{
Instance = sceneConfigJson.Deserialize<SceneConfigData>();
foreach (var config in Instance.List)
{
config.Initialize();
Instance._configs.TryAdd(config.Id, config);
Instance._sceneConfigByProcess.Add(config.ProcessConfigId, config);
Instance._sceneConfigBySceneType.Add(config.SceneType, config);
var configWorldConfigId = (int)config.WorldConfigId;
if (!Instance._worldSceneTypes.TryGetValue(configWorldConfigId, out var sceneConfigDic))
{
sceneConfigDic = new Dictionary<int, List<SceneConfig>>();
Instance._worldSceneTypes.Add(configWorldConfigId, sceneConfigDic);
}
if (!sceneConfigDic.TryGetValue(config.SceneType, out var sceneConfigList))
{
sceneConfigList = new List<SceneConfig>();
sceneConfigDic.Add(config.SceneType, sceneConfigList);
}
sceneConfigList.Add(config);
}
}
/// <summary>
/// 根据Id获取SceneConfig
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
public SceneConfig Get(uint id)
{
if (_configs.TryGetValue(id, out var sceneConfigInfo))
{
return sceneConfigInfo;
}
throw new FileNotFoundException($"WorldConfig not find {id} Id");
}
/// <summary>
/// 根据Id获取SceneConfig
/// </summary>
/// <param name="id"></param>
/// <param name="config"></param>
/// <returns></returns>
public bool TryGet(uint id, out SceneConfig config)
{
return _configs.TryGetValue(id, out config);
}
/// <summary>
/// 获得SceneConfig
/// </summary>
/// <param name="serverConfigId"></param>
/// <returns></returns>
public List<SceneConfig> GetByProcess(uint serverConfigId)
{
return _sceneConfigByProcess.TryGetValue(serverConfigId, out var list) ? list : new List<SceneConfig>();
}
/// <summary>
/// 获得SceneConfig
/// </summary>
/// <param name="sceneType"></param>
/// <returns></returns>
public List<SceneConfig> GetSceneBySceneType(int sceneType)
{
return !_sceneConfigBySceneType.TryGetValue(sceneType, out var list) ? new List<SceneConfig>() : list;
}
/// <summary>
/// 获得SceneConfig
/// </summary>
/// <param name="world"></param>
/// <param name="sceneType"></param>
/// <returns></returns>
public List<SceneConfig> GetSceneBySceneType(int world, int sceneType)
{
if (!_worldSceneTypes.TryGetValue(world, out var sceneConfigDic))
{
return new List<SceneConfig>();
}
if (!sceneConfigDic.TryGetValue(sceneType, out var list))
{
return new List<SceneConfig>();
}
return list;
}
}
/// <summary>
/// 表示一个Scene配置信息
/// </summary>
public sealed class SceneConfig
{
/// <summary>
/// ID
/// </summary>
public uint Id { get; set; }
/// <summary>
/// 进程Id
/// </summary>
public uint ProcessConfigId { get; set; }
/// <summary>
/// 世界Id
/// </summary>
public uint WorldConfigId { get; set; }
/// <summary>
/// Scene运行类型
/// </summary>
public string SceneRuntimeMode { get; set; }
/// <summary>
/// Scene类型
/// </summary>
public string SceneTypeString { get; set; }
/// <summary>
/// 协议类型
/// </summary>
public string NetworkProtocol { get; set; }
/// <summary>
/// 外网端口
/// </summary>
public int OuterPort { get; set; }
/// <summary>
/// 内网端口
/// </summary>
public int InnerPort { get; set; }
/// <summary>
/// Scene类型
/// </summary>
public int SceneType { get; set; }
/// <summary>
/// RouteId
/// </summary>
[JsonIgnore]
[IgnoreDataMember]
public long RouteId { get; private set; }
/// <summary>
/// 初始化方法
/// </summary>
public void Initialize()
{
RouteId = IdFactoryHelper.RuntimeId(0, Id, (byte)WorldConfigId, 0);
}
}
}
#endif

View File

@@ -0,0 +1,93 @@
#if FANTASY_NET
using System.Collections.Concurrent;
using System.Runtime.Serialization;
using Fantasy.Helper;
using Newtonsoft.Json;
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace Fantasy.Platform.Net
{
/// <summary>
/// 存放所有WorldConfigInfo信息
/// </summary>
public sealed class WorldConfigData
{
/// <summary>
/// 存放所有WorldConfigInfo信息
/// </summary>
public List<WorldConfig> List;
[JsonIgnore]
[IgnoreDataMember]
private readonly ConcurrentDictionary<uint, WorldConfig> _configs = new ConcurrentDictionary<uint, WorldConfig>();
/// <summary>
/// 获得WorldConfig的实例
/// </summary>
public static WorldConfigData Instance { get; private set; }
/// <summary>
/// 初始化WorldConfig
/// </summary>
/// <param name="worldConfigJson"></param>
public static void Initialize(string worldConfigJson)
{
Instance = worldConfigJson.Deserialize<WorldConfigData>();
foreach (var config in Instance.List)
{
Instance._configs.TryAdd(config.Id, config);
}
}
/// <summary>
/// 根据Id获取WorldConfig
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
public WorldConfig Get(uint id)
{
if (_configs.TryGetValue(id, out var worldConfigInfo))
{
return worldConfigInfo;
}
throw new FileNotFoundException($"WorldConfig not find {id} Id");
}
/// <summary>
/// 根据Id获取WorldConfig
/// </summary>
/// <param name="id"></param>
/// <param name="config"></param>
/// <returns></returns>
public bool TryGet(uint id, out WorldConfig config)
{
return _configs.TryGetValue(id, out config);
}
}
/// <summary>
/// 表示一个世界配置信息
/// </summary>
public sealed class WorldConfig
{
/// <summary>
/// Id
/// </summary>
public uint Id { get; set; }
/// <summary>
/// 名称
/// </summary>
public string WorldName { get; set; }
/// <summary>
/// 数据库连接字符串
/// </summary>
public string DbConnection { get; set; }
/// <summary>
/// 数据库名称
/// </summary>
public string DbName { get; set; }
/// <summary>
/// 数据库类型
/// </summary>
public string DbType { get; set; }
}
}
#endif

View File

@@ -0,0 +1,124 @@
#if FANTASY_NET
using CommandLine;
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.Helper;
using Fantasy.IdFactory;
using Fantasy.LowLevel;
using Fantasy.Network;
using Fantasy.Serialize;
// ReSharper disable FunctionNeverReturns
namespace Fantasy.Platform.Net;
/// <summary>
/// Fantasy.Net 应用程序入口
/// </summary>
/// <exception cref="Exception">当命令行格式异常时抛出。</exception>
/// <exception cref="NotSupportedException">不支持的 ProcessType 类型异常。</exception>
public static class Entry
{
/// <summary>
/// 框架初始化
/// </summary>
/// <param name="assemblies">注册的Assembly</param>
public static async FTask Initialize(params System.Reflection.Assembly[] assemblies)
{
// 解析命令行参数
Parser.Default.ParseArguments<CommandLineOptions>(Environment.GetCommandLineArgs())
.WithNotParsed(error => throw new Exception("Command line format error!"))
.WithParsed(option =>
{
ProcessDefine.Options = option;
ProcessDefine.InnerNetwork = Enum.Parse<NetworkProtocolType>(option.InnerNetwork);
});
// 初始化Log系统
Log.Initialize();
// 检查启动参数,后期可能有机器人等不同的启动参数
switch (ProcessDefine.Options.ProcessType)
{
case "Game":
{
break;
}
default:
{
throw new NotSupportedException($"ProcessType is {ProcessDefine.Options.ProcessType} Unrecognized!");
}
}
// 初始化程序集管理系统
await AssemblySystem.InnerInitialize(assemblies);
// 初始化序列化
SerializerManager.Initialize();
// 精度处理只针对Windows下有作用、其他系统没有这个问题、一般也不会用Windows来做服务器的
WinPeriod.Initialize();
FantasyMemory.Initialize();
}
/// <summary>
/// 启动Fantasy.Net
/// </summary>
public static async FTask Start()
{
// 启动Process
StartProcess().Coroutine();
await FTask.CompletedTask;
while (true)
{
ThreadScheduler.Update();
Thread.Sleep(1);
}
}
/// <summary>
/// 初始化并且启动框架
/// </summary>
/// <param name="assemblies"></param>
public static async FTask Start(params System.Reflection.Assembly[] assemblies)
{
await Initialize(assemblies);
await Start();
}
private static async FTask StartProcess()
{
if (ProcessDefine.Options.StartupGroup != 0)
{
foreach (var processConfig in ProcessConfigData.Instance.ForEachByStartupGroup((uint)ProcessDefine.Options.StartupGroup))
{
await Process.Create(processConfig.Id);
}
return;
}
switch (ProcessDefine.Options.Mode)
{
case "Develop":
{
foreach (var processConfig in ProcessConfigData.Instance.List)
{
await Process.Create(processConfig.Id);
}
return;
}
case "Release":
{
await Process.Create(ProcessDefine.Options.ProcessId);
return;
}
}
}
/// <summary>
/// 关闭 Fantasy
/// </summary>
public static void Close()
{
AssemblySystem.Dispose();
SerializerManager.Dispose();
}
}
#endif

View File

@@ -0,0 +1,156 @@
#if FANTASY_NET
using System.Collections.Concurrent;
using Fantasy.Async;
using Fantasy.IdFactory;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning disable CS8601 // Possible null reference assignment.
namespace Fantasy.Platform.Net;
/// <summary>
/// 一个进程的实例
/// </summary>
public sealed class Process : IDisposable
{
/// <summary>
/// 当前进程的Id
/// </summary>
public readonly uint Id;
/// <summary>
/// 进程关联的MachineId
/// </summary>
public readonly uint MachineId;
private readonly ConcurrentDictionary<uint, Scene> _processScenes = new ConcurrentDictionary<uint, Scene>();
private static readonly ConcurrentDictionary<uint, Scene> Scenes = new ConcurrentDictionary<uint, Scene>();
private Process() {}
private Process(uint id, uint machineId)
{
Id = id;
MachineId = machineId;
}
internal bool IsProcess(ref long routeId)
{
var sceneId = IdFactoryHelper.RuntimeIdTool.GetSceneId(ref routeId);
return _processScenes.ContainsKey(sceneId);
}
internal bool IsProcess(ref uint sceneId)
{
return _processScenes.ContainsKey(sceneId);
}
internal void AddSceneToProcess(Scene scene)
{
_processScenes.TryAdd(scene.SceneConfigId, scene);
}
internal void RemoveSceneToProcess(Scene scene, bool isDispose)
{
if (!_processScenes.Remove(scene.SceneConfigId, out _))
{
return;
}
if (isDispose)
{
scene.Dispose();
}
}
internal bool TryGetSceneToProcess(long routeId, out Scene scene)
{
var sceneId = IdFactoryHelper.RuntimeIdTool.GetSceneId(ref routeId);
return _processScenes.TryGetValue(sceneId, out scene);
}
internal bool TryGetSceneToProcess(uint sceneId, out Scene scene)
{
return _processScenes.TryGetValue(sceneId, out scene);
}
/// <summary>
/// 销毁方法
/// </summary>
public void Dispose()
{
if (_processScenes.IsEmpty)
{
return;
}
var sceneQueue = new Queue<Scene>();
foreach (var (_, scene) in _processScenes)
{
sceneQueue.Enqueue(scene);
}
while (sceneQueue.TryDequeue(out var removeScene))
{
removeScene.Dispose();
}
_processScenes.Clear();
}
internal static async FTask<Process?> Create(uint processConfigId)
{
if (!ProcessConfigData.Instance.TryGet(processConfigId, out var processConfig))
{
Log.Error($"not found processConfig by Id:{processConfigId}");
return null;
}
if (!MachineConfigData.Instance.TryGet(processConfig.MachineId, out var machineConfig))
{
Log.Error($"not found machineConfig by Id:{processConfig.MachineId}");
return null;
}
var process = new Process(processConfigId, processConfig.MachineId);
var sceneConfigs = SceneConfigData.Instance.GetByProcess(processConfigId);
foreach (var sceneConfig in sceneConfigs)
{
await Scene.Create(process, machineConfig, sceneConfig);
}
Log.Info($"Process:{processConfigId} Startup Complete SceneCount:{sceneConfigs.Count}");
return process;
}
internal bool IsInAppliaction(ref uint sceneId)
{
return _processScenes.ContainsKey(sceneId);
}
internal static void AddScene(Scene scene)
{
Scenes.TryAdd(scene.SceneConfigId, scene);
}
internal static void RemoveScene(Scene scene, bool isDispose)
{
if (!Scenes.Remove(scene.SceneConfigId, out _))
{
return;
}
if (isDispose)
{
scene.Dispose();
}
}
internal static bool TryGetScene(long routeId, out Scene scene)
{
var sceneId = IdFactoryHelper.RuntimeIdTool.GetSceneId(ref routeId);
return Scenes.TryGetValue(sceneId, out scene);
}
internal static bool TryGetScene(uint sceneId, out Scene scene)
{
return Scenes.TryGetValue(sceneId, out scene);
}
}
#endif

View File

@@ -0,0 +1,99 @@
#if FANTASY_NET
using CommandLine;
using Fantasy.Network;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace Fantasy.Platform.Net;
/// <summary>
/// Process运行模式
/// </summary>
public enum ProcessMode
{
/// <summary>
/// 默认
/// </summary>
None =0,
/// <summary>
/// 开发模式
/// </summary>
Develop = 1,
/// <summary>
/// 发布模式
/// </summary>
Release = 2
}
internal sealed class CommandLineOptions
{
/// <summary>
/// 用于启动指定的进程,该进程的 ID 与 ProcessConfig 的 ID 相关联。此参数只能传递单个 ID不支持传递多个 ID。
/// </summary>
[Option("pid", Required = false, Default = (uint)0, HelpText = "Enter an ProcessIdId such as 1")]
public uint ProcessId { get; set; }
/// <summary>
/// Process类型获取或设置应用程序的类型。
/// Game - 游戏服务器Process
/// Robot - 机器人(暂未支持该功能)
/// </summary>
[Option('a', "ProcessType", Required = false, Default = "Game", HelpText = "Game")]
public string ProcessType { get; set; }
/// <summary>
/// 服务器运行模式,获取或设置服务器的运行模式。
/// Develop - 开发模式启动Process配置表中的所有Process
/// Release - 发布模式根据ProcessId启动Process
/// </summary>
[Option('m', "Mode", Required = true, Default = "Release", HelpText = "Develop:启动Process配置表中的所有Process,\nRelease:根据ProcessId启动Process")]
public string Mode { get; set; }
/// <summary>
/// 服务器内部网络协议
/// TCP - 服务器内部之间通讯使用TCP协议
/// KCP - 服务器内部之间通讯使用KCP协议
/// WebSocket - 服务器内部之间通讯使用WebSocket协议(不推荐、TCP或KCP)
/// </summary>
[Option('n', "InnerNetwork", Required = false, Default = "TCP", HelpText = "TCP、KCP、WebSocket")]
public string InnerNetwork { get; set; }
/// <summary>
/// 会话空闲检查超时时间。
/// </summary>
[Option('t', "SessionIdleCheckerTimeout", Required = false, Default = 8000, HelpText = "Session idle check timeout")]
public int SessionIdleCheckerTimeout { get; set; }
/// <summary>
/// 会话空闲检查间隔。
/// </summary>
[Option('i', "SessionIdleCheckerInterval", Required = false, Default = 5000, HelpText = "Session idle check interval")]
public int SessionIdleCheckerInterval { get; set; }
/// <summary>
/// 启动组。
/// </summary>
[Option('g', "StartupGroup", Required = false, Default = 0, HelpText = "Used to start a group of Process")]
public int StartupGroup { get; set; }
}
/// <summary>
/// AppDefine
/// </summary>
internal static class ProcessDefine
{
/// <summary>
/// 命令行选项
/// </summary>
public static CommandLineOptions Options;
/// <summary>
/// App程序Id
/// </summary>
public static uint ProcessId => Options.ProcessId;
/// <summary>
/// 会话空闲检查超时时间。
/// </summary>
public static int SessionIdleCheckerTimeout => Options.SessionIdleCheckerTimeout;
/// <summary>
/// 会话空闲检查间隔。
/// </summary>
public static int SessionIdleCheckerInterval => Options.SessionIdleCheckerInterval;
/// <summary>
/// 内部网络通讯协议类型
/// </summary>
public static NetworkProtocolType InnerNetwork;
}
#endif

View File

@@ -0,0 +1,52 @@
#if FANTASY_NET
using System.Collections.Concurrent;
#pragma warning disable CS8765
#pragma warning disable CS8601
#pragma warning disable CS8618
namespace Fantasy;
/// <summary>
/// 线程的同步上下文
/// </summary>
public sealed class ThreadSynchronizationContext : SynchronizationContext
{
private readonly ConcurrentQueue<Action> _queue = new();
/// <summary>
/// 执行当前上下文投递过的逻辑
/// </summary>
public void Update()
{
while (_queue.TryDequeue(out var actionHandler))
{
try
{
actionHandler();
}
catch (Exception e)
{
Log.Error(e);
}
}
}
/// <summary>
/// 投递一个逻辑到当前上下文
/// </summary>
/// <param name="callback"></param>
/// <param name="state"></param>
public override void Post(SendOrPostCallback callback, object state)
{
Post(() => callback(state));
}
/// <summary>
/// 投递一个逻辑到当前上下文
/// </summary>
/// <param name="action"></param>
public void Post(Action action)
{
_queue.Enqueue(action);
}
}
#endif

View File

@@ -0,0 +1,19 @@
#if FANTASY_UNITY
namespace Fantasy.Platform.Unity
{
public static class AppDefine
{
public static string RemoteUpdatePath;
public static bool EditorModel = true;
public const string VersionName = "version.bytes";
public const string VersionMD5Name = "version.md5";
public const string AssetBundleManifestName = "Fantasy";
public static bool IsEditor => UnityEngine.Application.isEditor && EditorModel;
public static string AssetBundleSaveDirectory => "Assets/AssetBundles";
public static string LocalAssetBundlePath => UnityEngine.Application.streamingAssetsPath;
public static string RemoteAssetBundlePath => UnityEngine.Application.persistentDataPath;
public static string PersistentDataVersion => $"{UnityEngine.Application.persistentDataPath}/{VersionName}";
public static string StreamingAssetsVersion => $"{UnityEngine.Application.streamingAssetsPath}/{VersionName}";
}
}
#endif

View File

@@ -0,0 +1,11 @@
#if FANTASY_UNITY
using System;
namespace MongoDB.Bson.Serialization.Attributes
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class BsonDefaultValueAttribute : Attribute
{
public BsonDefaultValueAttribute(object defaultValue) { }
}
}
#endif

View File

@@ -0,0 +1,13 @@
#if FANTASY_UNITY
using System;
namespace MongoDB.Bson.Serialization.Attributes
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class BsonElementAttribute : Attribute
{
public BsonElementAttribute() { }
public BsonElementAttribute(string elementName) { }
}
}
#endif

View File

@@ -0,0 +1,11 @@
#if FANTASY_UNITY
using System;
namespace MongoDB.Bson.Serialization.Attributes
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class BsonIdAttribute : Attribute
{
}
}
#endif

View File

@@ -0,0 +1,11 @@
#if FANTASY_UNITY
using System;
namespace MongoDB.Bson.Serialization.Attributes
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class BsonIgnoreAttribute : Attribute
{
}
}
#endif

View File

@@ -0,0 +1,13 @@
#if FANTASY_UNITY
using System;
namespace MongoDB.Bson.Serialization.Attributes
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class BsonIgnoreIfDefaultAttribute : Attribute
{
public BsonIgnoreIfDefaultAttribute() { }
public BsonIgnoreIfDefaultAttribute(bool value) { }
}
}
#endif

View File

@@ -0,0 +1,11 @@
#if FANTASY_UNITY
using System;
namespace MongoDB.Bson.Serialization.Attributes
{
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class BsonIgnoreIfNullAttribute : Attribute
{
}
}
#endif

View File

@@ -0,0 +1,102 @@
#if FANTASY_UNITY
using System.Linq;
using Fantasy.Assembly;
using Fantasy.Async;
using Fantasy.Serialize;
using UnityEngine;
using UnityEngine.Scripting;
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8603 // Possible null reference return.
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
namespace Fantasy.Platform.Unity
{
public sealed class FantasyObject : MonoBehaviour
{
public static GameObject FantasyObjectGameObject { get; private set; }
public static void OnRuntimeMethodLoad()
{
FantasyObjectGameObject = new GameObject("Fantasy.Net");
DontDestroyOnLoad(FantasyObjectGameObject);
}
private void OnApplicationQuit()
{
Destroy(FantasyObjectGameObject);
}
}
public struct OnSceneCreate
{
public Scene Scene;
public object Arg;
}
public class Entry : MonoBehaviour
{
private static bool _isInit;
public static Scene Scene { get; private set; }
/// <summary>
/// 初始化框架
/// </summary>
/// <param name="assemblies"></param>
public static async FTask Initialize(params System.Reflection.Assembly[] assemblies)
{
if (_isInit)
{
Log.Error("Fantasy has already been initialized and does not need to be initialized again!");
return;
}
FantasyObject.OnRuntimeMethodLoad();
Log.Register(new UnityLog());
await AssemblySystem.InnerInitialize(assemblies);
// 初始化序列化
SerializerManager.Initialize();
#if FANTASY_WEBGL
ThreadSynchronizationContext.Initialize();
#endif
_isInit = true;
FantasyObject.FantasyObjectGameObject.AddComponent<Entry>();
Log.Debug("Fantasy Initialize Complete!");
}
/// <summary>
/// 在Entry中创建一个Scene如果Scene已经被创建过将先销毁Scene再创建。
/// </summary>
/// <param name="arg"></param>
/// <param name="sceneRuntimeMode"></param>
/// <returns></returns>
public static async FTask<Scene> CreateScene(object arg = null, string sceneRuntimeMode = SceneRuntimeMode.MainThread)
{
Scene?.Dispose();
Scene = await Scene.Create(sceneRuntimeMode);
await Scene.EventComponent.PublishAsync(new OnSceneCreate()
{
Arg = arg,
Scene = Scene
});
return Scene;
}
private void Update()
{
ThreadScheduler.Update();
}
private void OnDestroy()
{
AssemblySystem.Dispose();
SerializerManager.Dispose();
if (Scene != null)
{
Scene?.Dispose();
Scene = null;
}
_isInit = false;
}
}
}
#endif

View File

@@ -0,0 +1,108 @@
// using System.Reflection;
// using Fantasy.Assembly;
// using Fantasy.Async;
// // using UnityEngine;
// #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
// #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
// #pragma warning disable CS8603 // Possible null reference return.
// #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
// #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
//
// namespace Fantasy.Platform.Unity
// {
// public class MonoBehaviour
// {
//
// }
//
// public class GameObject
// {
// public GameObject(string name)
// {
//
// }
// }
//
// internal enum RuntimeInitializeLoadType
// {
// BeforeSceneLoad = 1,
// }
//
// internal class RuntimeInitializeOnLoadMethodAttribute : Attribute
// {
// public RuntimeInitializeLoadType RuntimeInitializeLoadType;
//
// public RuntimeInitializeOnLoadMethodAttribute(RuntimeInitializeLoadType loadType)
// {
//
// }
// }
//
// public sealed class FantasyObject : MonoBehaviour
// {
// public static GameObject FantasyObjectGameObject { get; private set; }
// // 这个方法将在游戏启动时自动调用
// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
// static void OnRuntimeMethodLoad()
// {
// FantasyObjectGameObject = new GameObject("Fantasy.Net");
// // DontDestroyOnLoad(FantasyObjectGameObject);
// }
// private void OnApplicationQuit()
// {
// // Destroy(FantasyObjectGameObject);
// }
// }
//
// public struct OnFantasyInit
// {
// public Scene Scene;
// }
//
// public class Entry : MonoBehaviour
// {
// private static bool _isInit;
// public static Scene Scene { get; private set; }
// /// <summary>
// /// 初始化框架
// /// </summary>
// public static async FTask<Scene> Initialize(params System.Reflection.Assembly[] assemblies)
// {
// Scene?.Dispose();
// // 初始化程序集管理系统
// AssemblySystem.Initialize(assemblies);
// if (!_isInit)
// {
// #if FANTASY_WEBGL
// ThreadSynchronizationContext.Initialize();
// #endif
// _isInit = true;
// // FantasyObject.FantasyObjectGameObject.AddComponent<Entry>();
// }
// // Scene = await Scene.Create(SceneRuntimeType.MainThread);
// // await Scene.EventComponent.PublishAsync(new OnFantasyInit()
// // {
// // Scene = Scene
// // });
// // return Scene;
// await FTask.CompletedTask;
// return null;
// }
//
// private void Update()
// {
// ThreadScheduler.Update();
// }
//
// private void OnDestroy()
// {
// AssemblySystem.Dispose();
// if (Scene != null)
// {
// Scene?.Dispose();
// Scene = null;
// }
// _isInit = false;
// }
// }
// }

View File

@@ -0,0 +1,104 @@
#if FANTASY_UNITY && !FANTASY_WEBGL
#pragma warning disable CS8765
#pragma warning disable CS8601
#pragma warning disable CS8618
using System;
using System.Collections.Concurrent;
using System.Threading;
namespace Fantasy
{
public sealed class ThreadSynchronizationContext : SynchronizationContext
{
private Action _actionHandler;
private readonly ConcurrentQueue<Action> _queue = new();
public void Update()
{
while (_queue.TryDequeue(out _actionHandler))
{
try
{
_actionHandler();
}
catch (Exception e)
{
Log.Error(e);
}
}
}
public override void Post(SendOrPostCallback callback, object state)
{
Post(() => callback(state));
}
public void Post(Action action)
{
_queue.Enqueue(action);
}
}
}
#endif
#if FANTASY_UNITY && FANTASY_WEBGL
using System;
using System.Collections.Generic;
using System.Threading;
using Fantasy;
using UnityEngine;
using Object = UnityEngine.Object;
public class WebGLSynchronizationContextUpdater : MonoBehaviour
{
private ThreadSynchronizationContext _context;
public void Initialize(ThreadSynchronizationContext context)
{
_context = context;
}
void Update()
{
_context.Update();
}
}
public sealed class ThreadSynchronizationContext : SynchronizationContext
{
private Action _actionHandler;
private readonly Queue<Action> _queue = new();
public static void Initialize()
{
var context = new ThreadSynchronizationContext();
SetSynchronizationContext(context);
var go = new GameObject("WebGLSynchronizationContextUpdater");
go.AddComponent<WebGLSynchronizationContextUpdater>().Initialize(context);
Object.DontDestroyOnLoad(go);
}
public void Update()
{
while (_queue.TryDequeue(out _actionHandler))
{
try
{
_actionHandler();
}
catch (Exception e)
{
Log.Error(e);
}
}
}
public override void Post(SendOrPostCallback callback, object state)
{
Post(() => callback(state));
}
public void Post(Action action)
{
_queue.Enqueue(action);
}
}
#endif