Files
Fishing2Server/Fantasy/Fantays.Console/Runtime/Core/Scene/Scene.cs
2025-06-30 10:51:37 +08:00

619 lines
24 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using Fantasy.Async;
using Fantasy.Entitas;
using Fantasy.Event;
using Fantasy.IdFactory;
using Fantasy.Network;
using Fantasy.Network.Interface;
using Fantasy.Pool;
using Fantasy.Scheduler;
using Fantasy.Timer;
#if FANTASY_NET
using Fantasy.DataBase;
using Fantasy.Platform.Net;
using Fantasy.SingleCollection;
using System.Runtime.CompilerServices;
using Fantasy.Network.Route;
using Fantasy.Network.Roaming;
#endif
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
#pragma warning disable CS8601 // Possible null reference assignment.
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
#pragma warning disable CS8603 // Possible null reference return.
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
#pragma warning disable CS8602 // Dereference of a possibly null reference.
#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.
namespace Fantasy
{
/// <summary>
/// 当Scene创建完成后发送的事件参数
/// </summary>
public struct OnCreateScene
{
/// <summary>
/// 获取与事件关联的场景实体。
/// </summary>
public readonly Scene Scene;
/// <summary>
/// 初始化一个新的 OnCreateScene 实例。
/// </summary>
/// <param name="scene"></param>
public OnCreateScene(Scene scene)
{
Scene = scene;
}
}
/// <summary>
/// 表示一个场景实体,用于创建与管理特定的游戏场景信息。
/// </summary>
public partial class Scene : Entity
{
#region Members
/// <summary>
/// Scene的运行类型
/// </summary>
public SceneRuntimeType SceneRuntimeType { get; protected set; }
#if FANTASY_NET
/// <summary>
/// Scene类型对应SceneConfig的SceneType
/// </summary>
public int SceneType { get; protected set; }
/// <summary>
/// 所属的世界
/// </summary>
public World World { get; protected set; }
/// <summary>
/// 所在的Process
/// </summary>
public Process Process { get; protected set; }
/// <summary>
/// SceneConfig的Id
/// </summary>
public uint SceneConfigId { get; protected set; }
internal ANetwork InnerNetwork { get; private set; }
internal ANetwork OuterNetwork { get; private set; }
internal SceneConfig SceneConfig => SceneConfigData.Instance.Get(SceneConfigId);
private readonly Dictionary<uint, ProcessSessionInfo> _processSessionInfos = new Dictionary<uint, ProcessSessionInfo>();
#endif
/// <summary>
/// 当前Scene的上下文
/// </summary>
public ThreadSynchronizationContext ThreadSynchronizationContext { get; internal set; }
/// <summary>
/// 当前Scene的下创建的Entity
/// </summary>
private readonly Dictionary<long, Entity> _entities = new Dictionary<long, Entity>();
internal readonly Dictionary<Type, Func<IPool>> TypeInstance = new Dictionary<Type, Func<IPool>>();
#endregion
#region IdFactory
/// <summary>
/// Entity实体Id的生成器
/// </summary>
public IEntityIdFactory EntityIdFactory { get; protected set; }
/// <summary>
/// Entity实体RuntimeId的生成器
/// </summary>
public IRuntimeIdFactory RuntimeIdFactory { get; protected set; }
#endregion
#region Pool
internal EntityPool EntityPool;
internal EntityListPool<Entity> EntityListPool;
internal EntitySortedDictionaryPool<long, Entity> EntitySortedDictionaryPool;
#endregion
#region Component
/// <summary>
/// Scene下的任务调度器系统组件
/// </summary>
public TimerComponent TimerComponent { get; internal set; }
/// <summary>
/// Scene下的事件系统组件
/// </summary>
public EventComponent EventComponent { get; internal set; }
/// <summary>
/// Scene下的ESC系统组件
/// </summary>
public EntityComponent EntityComponent { get; internal set; }
/// <summary>
/// Scene下的网络消息对象池组件
/// </summary>
public MessagePoolComponent MessagePoolComponent { get; internal set; }
/// <summary>
/// Scene下的协程锁组件
/// </summary>
public CoroutineLockComponent CoroutineLockComponent { get; internal set; }
/// <summary>
/// Scene下的网络消息派发组件
/// </summary>
internal MessageDispatcherComponent MessageDispatcherComponent { get; set; }
#if FANTASY_NET
/// <summary>
/// Scene下的Entity分表组件
/// </summary>
public SingleCollectionComponent SingleCollectionComponent { get; internal set; }
/// <summary>
/// Scene下的内网消息发送组件
/// </summary>
public NetworkMessagingComponent NetworkMessagingComponent { get; internal set; }
/// <summary>
/// Scene下的漫游终端管理组件
/// </summary>
public TerminusComponent TerminusComponent { get; internal set; }
/// <summary>
/// Scene下的Session漫游组件
/// </summary>
public RoamingComponent RoamingComponent { get; internal set; }
#endif
#endregion
#region Initialize
private async FTask Initialize()
{
EntityPool = new EntityPool();
EntityListPool = new EntityListPool<Entity>();
EntitySortedDictionaryPool = new EntitySortedDictionaryPool<long, Entity>();
SceneUpdate = EntityComponent = await Create<EntityComponent>(this, false, false).Initialize();
MessagePoolComponent = Create<MessagePoolComponent>(this,false,true);
EventComponent = await Create<EventComponent>(this,false,true).Initialize();
TimerComponent = Create<TimerComponent>(this, false, true).Initialize();
CoroutineLockComponent = Create<CoroutineLockComponent>(this, false, true).Initialize();
MessageDispatcherComponent = await Create<MessageDispatcherComponent>(this, false, true).Initialize();
#if FANTASY_NET
NetworkMessagingComponent = Create<NetworkMessagingComponent>(this, false, true);
SingleCollectionComponent = await Create<SingleCollectionComponent>(this, false, true).Initialize();
TerminusComponent = Create<TerminusComponent>(this, false, true);
RoamingComponent = Create<RoamingComponent>(this, false, true).Initialize();
#endif
}
/// <summary>
/// Scene销毁方法执行了该方法会把当前Scene下的所有实体都销毁掉。
/// </summary>
public override void Dispose()
{
if (IsDisposed)
{
return;
}
base.Dispose();
_entities.Remove(RuntimeId);
switch (SceneRuntimeType)
{
case SceneRuntimeType.Root:
{
#if FANTASY_NET
foreach (var (_, processSessionInfo) in _processSessionInfos.ToList())
{
processSessionInfo.Dispose();
}
_processSessionInfos.Clear();
#endif
_entities.Remove(EntityComponent.RuntimeId);
foreach (var (runtimeId, entity) in _entities.ToList())
{
if (runtimeId != entity.RuntimeId)
{
continue;
}
entity.Dispose();
}
_entities.Clear();
#if FANTASY_UNITY
_unityWorldId--;
_unitySceneId--;
#endif
TypeInstance.Clear();
#if FANTASY_NET
Process.RemoveScene(this, false);
Process.RemoveSceneToProcess(this, false);
#endif
EntityComponent.Dispose();
EntityPool.Dispose();
EntityListPool.Dispose();
EntitySortedDictionaryPool.Dispose();
break;
}
case SceneRuntimeType.SubScene:
{
break;
}
default:
{
Log.Error($"SceneRuntimeType: {SceneRuntimeType} The unsupported SceneRuntimeType of the Scene executed Dispose.");
break;
}
}
SceneUpdate = null;
EntityIdFactory = null;
RuntimeIdFactory = null;
EntityPool = null;
EntityListPool = null;
EntitySortedDictionaryPool = null;
EntityComponent = null;
TimerComponent = null;
EventComponent = null;
MessagePoolComponent = null;
CoroutineLockComponent = null;
MessageDispatcherComponent = null;
#if FANTASY_NET
World = null;
Process = null;
SceneType = 0;
SceneConfigId = 0;
SingleCollectionComponent = null;
NetworkMessagingComponent = null;
TerminusComponent = null;
RoamingComponent = null;
#elif FANTASY_UNITY
Session = null;
UnityNetwork = null;
#endif
ThreadSynchronizationContext = null;
SceneRuntimeType = SceneRuntimeType.None;
}
#endregion
internal ISceneUpdate SceneUpdate { get; set; }
internal void Update()
{
try
{
SceneUpdate.Update();
}
catch (Exception e)
{
Log.Error(e);
}
}
#region Create
#if FANTASY_UNITY || FANTASY_CONSOLE
private static uint _unitySceneId = 0;
private static byte _unityWorldId = 0;
public Session Session { get; private set; }
private AClientNetwork UnityNetwork { get; set; }
/// <summary>
/// 创建一个Unity的Scene注意:该方法只能在主线程下使用。
/// </summary>
/// <param name="sceneRuntimeMode">选择Scene的运行方式</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static async FTask<Scene> Create(string sceneRuntimeMode = SceneRuntimeMode.MainThread)
{
var world = ++_unityWorldId;
if (world > byte.MaxValue - 1)
{
throw new Exception($"World ID ({world}) exceeds the maximum allowed value of 255.");
}
var sceneId = (uint)(++_unitySceneId + world * 1000);
if (sceneId > 255255)
{
throw new Exception($"Scene ID ({sceneId}) exceeds the maximum allowed value of 255255.");
}
var scene = new Scene();
scene.Scene = scene;
scene.Parent = scene;
scene.Type = typeof(Scene);
scene.SceneRuntimeType = SceneRuntimeType.Root;
scene.EntityIdFactory = IdFactoryHelper.EntityIdFactory(sceneId, world);
scene.RuntimeIdFactory = IdFactoryHelper.RuntimeIdFactory(0, sceneId, world);
scene.Id = IdFactoryHelper.EntityId(0, sceneId, world, 0);
scene.RuntimeId = IdFactoryHelper.RuntimeId(0, sceneId, world, 0);
scene.AddEntity(scene);
await SetScheduler(scene, sceneRuntimeMode);
scene.ThreadSynchronizationContext.Post(() =>
{
scene.EventComponent.PublishAsync(new OnCreateScene(scene)).Coroutine();
});
return scene;
}
public Session Connect(string remoteAddress, NetworkProtocolType networkProtocolType, Action onConnectComplete, Action onConnectFail, Action onConnectDisconnect, bool isHttps, int connectTimeout = 5000)
{
UnityNetwork?.Dispose();
UnityNetwork = NetworkProtocolFactory.CreateClient(this, networkProtocolType, NetworkTarget.Outer);
Session = UnityNetwork.Connect(remoteAddress, onConnectComplete, onConnectFail, onConnectDisconnect, isHttps, connectTimeout);
return Session;
}
#endif
#if FANTASY_NET
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Scene Create(Process process, byte worldId, uint sceneConfigId)
{
var scene = new Scene();
scene.Scene = scene;
scene.Parent = scene;
scene.Type = typeof(Scene);
scene.Process = process;
scene.SceneRuntimeType = SceneRuntimeType.Root;
scene.EntityIdFactory = IdFactoryHelper.EntityIdFactory(sceneConfigId, worldId);
scene.RuntimeIdFactory = IdFactoryHelper.RuntimeIdFactory(0,sceneConfigId, worldId);
scene.Id = IdFactoryHelper.EntityId(0, sceneConfigId, worldId, 0);
scene.RuntimeId = IdFactoryHelper.RuntimeId(0, sceneConfigId, worldId, 0);
scene.AddEntity(scene);
return scene;
}
/// <summary>
/// 创建一个新的Scene
/// </summary>
/// <param name="process">所属的Process</param>
/// <param name="machineConfig">对应的MachineConfig配置文件</param>
/// <param name="sceneConfig">对应的SceneConfig配置文件</param>
/// <returns>创建成功后会返回创建的Scene的实例</returns>
public static async FTask<Scene> Create(Process process, MachineConfig machineConfig, SceneConfig sceneConfig)
{
var scene = Create(process, (byte)sceneConfig.WorldConfigId, sceneConfig.Id);
scene.SceneType = sceneConfig.SceneType;
scene.SceneConfigId = sceneConfig.Id;
await SetScheduler(scene, sceneConfig.SceneRuntimeMode);
if (sceneConfig.WorldConfigId != 0)
{
scene.World = World.Create(scene, (byte)sceneConfig.WorldConfigId);
}
if (sceneConfig.InnerPort != 0)
{
// 创建内网网络服务器
scene.InnerNetwork = NetworkProtocolFactory.CreateServer(scene, ProcessDefine.InnerNetwork, NetworkTarget.Inner, machineConfig.InnerBindIP, sceneConfig.InnerPort);
}
if (sceneConfig.OuterPort != 0)
{
// 创建外网网络服务
var networkProtocolType = Enum.Parse<NetworkProtocolType>(sceneConfig.NetworkProtocol);
scene.OuterNetwork = NetworkProtocolFactory.CreateServer(scene, networkProtocolType, NetworkTarget.Outer, machineConfig.OuterBindIP, sceneConfig.OuterPort);
}
Process.AddScene(scene);
process.AddSceneToProcess(scene);
scene.ThreadSynchronizationContext.Post(() =>
{
if (sceneConfig.SceneTypeString == "Addressable")
{
// 如果是AddressableScene,自动添加上AddressableManageComponent。
scene.AddComponent<AddressableManageComponent>();
}
scene.EventComponent.PublishAsync(new OnCreateScene(scene)).Coroutine();
});
return scene;
}
/// <summary>
/// 在Scene下面创建一个子Scene一般用于副本或者一些特殊的场景。
/// </summary>
/// <param name="parentScene">主Scene的实例</param>
/// <param name="sceneType">SceneType可以在SceneType里找到例如:SceneType.Addressable</param>
/// <param name="onSubSceneComplete">子Scene创建成功后执行的委托可以传递null</param>
/// <returns></returns>
public static SubScene CreateSubScene(Scene parentScene, int sceneType, Action<SubScene, Scene> onSubSceneComplete = null)
{
var scene = new SubScene();
scene.Scene = scene;
scene.Parent = scene;
scene.RootScene = parentScene;
scene.Type = typeof(SubScene);
scene.SceneType = sceneType;
scene.World = parentScene.World;
scene.Process = parentScene.Process;
scene.SceneRuntimeType = SceneRuntimeType.SubScene;
scene.EntityIdFactory = parentScene.EntityIdFactory;
scene.RuntimeIdFactory = parentScene.RuntimeIdFactory;
scene.Id = scene.EntityIdFactory.Create;
scene.RuntimeId = scene.RuntimeIdFactory.Create;
scene.AddEntity(scene);
scene.Initialize(parentScene);
scene.ThreadSynchronizationContext.Post(() => OnEvent().Coroutine());
return scene;
async FTask OnEvent()
{
await scene.EventComponent.PublishAsync(new OnCreateScene(scene));
onSubSceneComplete?.Invoke(scene, parentScene);
}
}
#endif
private static async FTask SetScheduler(Scene scene, string sceneRuntimeMode)
{
switch (sceneRuntimeMode)
{
case "MainThread":
{
scene.ThreadSynchronizationContext = ThreadScheduler.MainScheduler.ThreadSynchronizationContext;
scene.SceneUpdate = new EmptySceneUpdate();
ThreadScheduler.AddMainScheduler(scene);
await scene.Initialize();
break;
}
case "MultiThread":
{
#if !FANTASY_WEBGL
scene.ThreadSynchronizationContext = new ThreadSynchronizationContext();
#endif
scene.SceneUpdate = new EmptySceneUpdate();
ThreadScheduler.AddToMultiThreadScheduler(scene);
await scene.Initialize();
break;
}
case "ThreadPool":
{
#if !FANTASY_WEBGL
scene.ThreadSynchronizationContext = new ThreadSynchronizationContext();
#endif
scene.SceneUpdate = new EmptySceneUpdate();
ThreadScheduler.AddToThreadPoolScheduler(scene);
await scene.Initialize();
break;
}
}
}
#endregion
#region Entities
/// <summary>
/// 添加一个实体到当前Scene下
/// </summary>
/// <param name="entity">实体实例</param>
public virtual void AddEntity(Entity entity)
{
_entities.Add(entity.RuntimeId, entity);
}
/// <summary>
/// 根据RunTimeId查询一个实体
/// </summary>
/// <param name="runTimeId">实体的RunTimeId</param>
/// <returns>返回的实体</returns>
public virtual Entity GetEntity(long runTimeId)
{
return _entities.TryGetValue(runTimeId, out var entity) ? entity : null;
}
/// <summary>
/// 根据RunTimeId查询一个实体
/// </summary>
/// <param name="runTimeId">实体的RunTimeId</param>
/// <param name="entity">实体实例</param>
/// <returns>返回一个bool值来提示是否查找到这个实体</returns>
public virtual bool TryGetEntity(long runTimeId, out Entity entity)
{
return _entities.TryGetValue(runTimeId, out entity);
}
/// <summary>
/// 根据RunTimeId查询一个实体
/// </summary>
/// <param name="runTimeId">实体的RunTimeId</param>
/// <typeparam name="T">要查询实体的泛型类型</typeparam>
/// <returns>返回的实体</returns>
public virtual T GetEntity<T>(long runTimeId) where T : Entity
{
return _entities.TryGetValue(runTimeId, out var entity) ? (T)entity : null;
}
/// <summary>
/// 根据RunTimeId查询一个实体
/// </summary>
/// <param name="runTimeId">实体的RunTimeId</param>
/// <param name="entity">实体实例</param>
/// <typeparam name="T">要查询实体的泛型类型</typeparam>
/// <returns>返回一个bool值来提示是否查找到这个实体</returns>
public virtual bool TryGetEntity<T>(long runTimeId, out T entity) where T : Entity
{
if (_entities.TryGetValue(runTimeId, out var getEntity))
{
entity = (T)getEntity;
return true;
}
entity = null;
return false;
}
/// <summary>
/// 删除一个实体,仅是删除不会指定实体的销毁方法
/// </summary>
/// <param name="runTimeId">实体的RunTimeId</param>
/// <returns>返回一个bool值来提示是否删除了这个实体</returns>
public virtual bool RemoveEntity(long runTimeId)
{
return _entities.Remove(runTimeId);
}
/// <summary>
/// 删除一个实体,仅是删除不会指定实体的销毁方法
/// </summary>
/// <param name="entity">实体实例</param>
/// <returns>返回一个bool值来提示是否删除了这个实体</returns>
public virtual bool RemoveEntity(Entity entity)
{
return _entities.Remove(entity.RuntimeId);
}
#endregion
#region InnerSession
#if FANTASY_NET
/// <summary>
/// 根据runTimeId获得Session
/// </summary>
/// <param name="runTimeId"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public virtual Session GetSession(long runTimeId)
{
var sceneId = IdFactoryHelper.RuntimeIdTool.GetSceneId(ref runTimeId);
if (_processSessionInfos.TryGetValue(sceneId, out var processSessionInfo))
{
if (!processSessionInfo.Session.IsDisposed)
{
return processSessionInfo.Session;
}
_processSessionInfos.Remove(sceneId);
}
if (Process.IsInAppliaction(ref sceneId))
{
// 如果在同一个Process下不需要通过Socket发送了直接通过Process下转发。
var processSession = Session.CreateInnerSession(Scene);
_processSessionInfos.Add(sceneId, new ProcessSessionInfo(processSession, null));
return processSession;
}
if (!SceneConfigData.Instance.TryGet(sceneId, out var sceneConfig))
{
throw new Exception($"The scene with sceneId {sceneId} was not found in the configuration file");
}
if (!ProcessConfigData.Instance.TryGet(sceneConfig.ProcessConfigId, out var processConfig))
{
throw new Exception($"The process with processId {sceneConfig.ProcessConfigId} was not found in the configuration file");
}
if (!MachineConfigData.Instance.TryGet(processConfig.MachineId, out var machineConfig))
{
throw new Exception($"The machine with machineId {processConfig.MachineId} was not found in the configuration file");
}
var remoteAddress = $"{machineConfig.InnerBindIP}:{sceneConfig.InnerPort}";
var client = NetworkProtocolFactory.CreateClient(Scene, ProcessDefine.InnerNetwork, NetworkTarget.Inner);
var session = client.Connect(remoteAddress, null, () =>
{
Log.Error($"Unable to connect to the target server sourceServerId:{Scene.Process.Id} targetServerId:{sceneConfig.ProcessConfigId}");
}, null, false);
_processSessionInfos.Add(sceneId, new ProcessSessionInfo(session, client));
return session;
}
#endif
#endregion
}
}