UserGrain.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Numerics;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using MineCase.Formats;
  7. using MineCase.Server.Game;
  8. using MineCase.Server.Game.Entities;
  9. using MineCase.Server.Network;
  10. using MineCase.Server.Network.Play;
  11. using MineCase.Server.World;
  12. using Orleans;
  13. using Orleans.Concurrency;
  14. namespace MineCase.Server.User
  15. {
  16. [Reentrant]
  17. internal class UserGrain : Grain, IUser
  18. {
  19. private string _name;
  20. private uint _protocolVersion;
  21. private string _worldId;
  22. private IWorld _world;
  23. private IClientboundPacketSink _sink;
  24. private ClientPlayPacketGenerator _generator;
  25. private IDisposable _sendKeepAliveTimer;
  26. private IDisposable _worldTimeSyncTimer;
  27. public HashSet<uint> _keepAliveWaiters;
  28. private readonly Random _keepAliveIdRand = new Random();
  29. private const int ClientKeepInterval = 6;
  30. private bool _isOnline = false;
  31. private DateTime _keepAliveRequestTime;
  32. private DateTime _keepAliveResponseTime;
  33. private UserState _state;
  34. private IPlayer _player;
  35. public override async Task OnActivateAsync()
  36. {
  37. if (string.IsNullOrEmpty(_worldId))
  38. {
  39. var world = await GrainFactory.GetGrain<IWorldAccessor>(0).GetDefaultWorld();
  40. _worldId = world.GetPrimaryKeyString();
  41. _world = world;
  42. }
  43. _world = await GrainFactory.GetGrain<IWorldAccessor>(0).GetWorld(_worldId);
  44. }
  45. public Task<IClientboundPacketSink> GetClientPacketSink()
  46. {
  47. return Task.FromResult(_sink);
  48. }
  49. public async Task<IGameSession> GetGameSession()
  50. {
  51. var world = await GetWorld();
  52. return GrainFactory.GetGrain<IGameSession>(world.GetPrimaryKeyString());
  53. }
  54. public Task<IWorld> GetWorld() => Task.FromResult(_world);
  55. public Task SetClientPacketSink(IClientboundPacketSink sink)
  56. {
  57. _sink = sink;
  58. _generator = new ClientPlayPacketGenerator(sink);
  59. return Task.CompletedTask;
  60. }
  61. public async Task JoinGame()
  62. {
  63. var playerEid = await _world.NewEntityId();
  64. _player = GrainFactory.GetGrain<IPlayer>(_world.MakeEntityKey(playerEid));
  65. await _player.SetName(_name);
  66. await _player.BindToUser(this);
  67. await _world.AttachEntity(_player);
  68. _state = UserState.JoinedGame;
  69. _keepAliveWaiters = new HashSet<uint>();
  70. _sendKeepAliveTimer = RegisterTimer(OnSendKeepAliveRequests, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
  71. // _worldTimeSyncTimer = RegisterTimer(OnSyncWorldTime, null, TimeSpan.Zero, )
  72. }
  73. private async Task SendTimeUpdate()
  74. {
  75. var time = await (await GetWorld()).GetTime();
  76. await _generator.TimeUpdate(time.age, time.timeOfDay);
  77. }
  78. public Task UseEntity(uint targetEid, EntityUsage type, Vector3? targetPosition, EntityInteractHand? hand)
  79. {
  80. return Task.CompletedTask;
  81. }
  82. private async Task OnSendKeepAliveRequests(object state)
  83. {
  84. if (_isOnline && _keepAliveWaiters.Count >= ClientKeepInterval)
  85. {
  86. _isOnline = false;
  87. KickPlayer().Ignore();
  88. }
  89. else
  90. {
  91. var id = (uint)_keepAliveIdRand.Next();
  92. _keepAliveWaiters.Add(id);
  93. _keepAliveRequestTime = DateTime.UtcNow;
  94. await _generator.KeepAlive(id);
  95. }
  96. }
  97. public Task KeepAlive(uint keepAliveId)
  98. {
  99. _keepAliveWaiters.Remove(keepAliveId);
  100. if (_keepAliveWaiters.Count == 0)
  101. _keepAliveResponseTime = DateTime.UtcNow;
  102. return Task.CompletedTask;
  103. }
  104. private async Task KickPlayer()
  105. {
  106. _sendKeepAliveTimer?.Dispose();
  107. _sendKeepAliveTimer = null;
  108. _worldTimeSyncTimer?.Dispose();
  109. _worldTimeSyncTimer = null;
  110. var game = await GetGameSession();
  111. await game.LeaveGame(this);
  112. await _sink.Close();
  113. DeactivateOnIdle();
  114. }
  115. public Task<IPlayer> GetPlayer() => Task.FromResult(_player);
  116. public async Task NotifyLoggedIn()
  117. {
  118. _isOnline = true;
  119. _keepAliveWaiters = new HashSet<uint>();
  120. await SendTimeUpdate();
  121. await _player.NotifyLoggedIn();
  122. _state = UserState.DownloadingWorld;
  123. }
  124. public Task SendChatMessage(Chat jsonData, byte position)
  125. {
  126. _generator.SendChatMessage(jsonData, position);
  127. return Task.CompletedTask;
  128. }
  129. public Task<String> GetName()
  130. {
  131. return Task.FromResult(_name);
  132. }
  133. public Task SetName(string name)
  134. {
  135. _name = name;
  136. return Task.CompletedTask;
  137. }
  138. public Task<uint> GetProtocolVersion()
  139. {
  140. return Task.FromResult(_protocolVersion);
  141. }
  142. public Task SetProtocolVersion(uint version)
  143. {
  144. _protocolVersion = version;
  145. return Task.CompletedTask;
  146. }
  147. public Task<uint> GetPing()
  148. {
  149. uint ping;
  150. var diff = DateTime.UtcNow - _keepAliveRequestTime;
  151. if (diff.Ticks < 0)
  152. ping = int.MaxValue;
  153. else
  154. ping = (uint)diff.TotalMilliseconds;
  155. return Task.FromResult(ping);
  156. }
  157. public async Task OnGameTick(TimeSpan deltaTime)
  158. {
  159. if (_state == UserState.DownloadingWorld)
  160. {
  161. await _player.SendPositionAndLook();
  162. _state = UserState.Playing;
  163. }
  164. if (_state >= UserState.JoinedGame && _state < UserState.Destroying)
  165. {
  166. for (int i = 0; i < 4; i++)
  167. {
  168. if (!await StreamNextChunk())
  169. break;
  170. }
  171. }
  172. }
  173. private Task<bool> StreamNextChunk()
  174. {
  175. return Task.FromResult(true);
  176. }
  177. private enum UserState : uint
  178. {
  179. None,
  180. JoinedGame,
  181. DownloadingWorld,
  182. Playing,
  183. Destroying
  184. }
  185. }
  186. }