diff --git a/Ragon.Common/Sources/RagonOperation.cs b/Ragon.Common/Sources/RagonOperation.cs index 1def00f..75960dd 100644 --- a/Ragon.Common/Sources/RagonOperation.cs +++ b/Ragon.Common/Sources/RagonOperation.cs @@ -20,11 +20,9 @@ namespace Ragon.Common PLAYER_JOINED, PLAYER_LEAVED, - CREATE_STATES, CREATE_ENTITY, + CREATE_SCENE_ENTITY, DESTROY_ENTITY, - CREATE_STATIC_ENTITY, - SNAPSHOT, REPLICATE_ENTITY_STATE, diff --git a/Ragon.Common/Sources/RagonSerializer.cs b/Ragon.Common/Sources/RagonSerializer.cs index 059d976..505688b 100644 --- a/Ragon.Common/Sources/RagonSerializer.cs +++ b/Ragon.Common/Sources/RagonSerializer.cs @@ -43,14 +43,20 @@ namespace Ragon.Common _size = _offset; _offset = 0; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AddOffset(int offset) + { + _offset += offset; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteByte(byte value) + public int WriteByte(byte value) { ResizeIfNeed(1); - _data[_offset] = value; _offset += 1; + return 1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -62,12 +68,12 @@ namespace Ragon.Common } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteBool(bool value) + public int WriteBool(bool value) { ResizeIfNeed(1); - _data[_offset] = value ? (byte) 1 : (byte) 0; _offset += 1; + return 1; } @@ -81,7 +87,7 @@ namespace Ragon.Common [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteInt(int value) + public int WriteInt(int value) { ResizeIfNeed(4); var converter = new ValueConverter() {Int = value}; @@ -90,6 +96,19 @@ namespace Ragon.Common _data[_offset + 2] = converter.Byte2; _data[_offset + 3] = converter.Byte3; _offset += 4; + return 4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int WriteInt(int value, int offset) + { + ResizeIfNeed(4); + var converter = new ValueConverter() {Int = value}; + _data[offset] = converter.Byte0; + _data[offset + 1] = converter.Byte1; + _data[offset + 2] = converter.Byte2; + _data[offset + 3] = converter.Byte3; + return 4; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -109,7 +128,7 @@ namespace Ragon.Common } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteLong(long value, int offset) + public int WriteLong(long value, int offset) { var converter = new ValueConverter() {Long = value}; _data[offset] = converter.Byte0; @@ -120,6 +139,7 @@ namespace Ragon.Common _data[offset + 5] = converter.Byte5; _data[offset + 6] = converter.Byte6; _data[offset + 7] = converter.Byte7; + return 8; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -141,10 +161,11 @@ namespace Ragon.Common } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteFloat(float value) + public int WriteFloat(float value) { var converter = new ValueConverter() {Float = value}; WriteInt(converter.Int); + return 4; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -157,39 +178,16 @@ namespace Ragon.Common } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteString(string value, ushort size) + public int WriteString(string value) { - var stringRaw = Encoding.UTF8.GetBytes(value).AsSpan(); - ResizeIfNeed(2 + size); - - WriteUShort((ushort) stringRaw.Length); - - var data = _data.AsSpan().Slice(_offset, size); - stringRaw.CopyTo(data); - _offset += stringRaw.Length; - } + var rawData = Encoding.UTF8.GetBytes(value).AsSpan(); + ResizeIfNeed(2 + rawData.Length); + WriteUShort((ushort) rawData.Length); + var data = _data.AsSpan().Slice(_offset, rawData.Length); + rawData.CopyTo(data); + _offset += rawData.Length; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public string ReadString(ushort size) - { - var lenght = ReadUShort(); - var stringRaw = _data.AsSpan().Slice(_offset, size); - var strData = stringRaw.Slice(0, lenght); - var str = Encoding.UTF8.GetString(strData); - _offset += size; - return str; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteString(string value) - { - var stringRaw = Encoding.UTF8.GetBytes(value).AsSpan(); - ResizeIfNeed(2 + stringRaw.Length); - - WriteUShort((ushort) stringRaw.Length); - var data = _data.AsSpan().Slice(_offset, stringRaw.Length); - stringRaw.CopyTo(data); - _offset += stringRaw.Length; + return rawData.Length + 2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -213,7 +211,7 @@ namespace Ragon.Common } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteData(ref ReadOnlySpan payload) + public int WriteData(ref ReadOnlySpan payload) { ResizeIfNeed(payload.Length); @@ -222,6 +220,7 @@ namespace Ragon.Common payload.CopyTo(payloadData); _offset += payload.Length; + return payload.Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -237,12 +236,14 @@ namespace Ragon.Common } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteOperation(RagonOperation ragonOperation) + public int WriteOperation(RagonOperation ragonOperation) { ResizeIfNeed(1); _data[_offset] = (byte) ragonOperation; _offset += 1; + + return 1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -254,13 +255,24 @@ namespace Ragon.Common } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void WriteUShort(ushort value) + public int WriteUShort(ushort value) { ResizeIfNeed(2); _data[_offset] = (byte) (value & 0x00FF); _data[_offset + 1] = (byte) ((value & 0xFF00) >> 8); _offset += 2; + + return 2; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int WriteUShort(ushort value, int offset) + { + ResizeIfNeed(2); + _data[offset] = (byte) (value & 0x00FF); + _data[offset + 1] = (byte) ((value & 0xFF00) >> 8); + return 2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Ragon.SimpleServer/Source/AuthorizationProvider.cs b/Ragon.SimpleServer/Source/AuthorizationProvider.cs index cb1bcf1..f263aef 100644 --- a/Ragon.SimpleServer/Source/AuthorizationProvider.cs +++ b/Ragon.SimpleServer/Source/AuthorizationProvider.cs @@ -12,7 +12,7 @@ public class AuthorizationProviderByKey: IAuthorizationProvider _configuration = configuration; } - public async Task OnAuthorizationRequest(string key, string name, byte protocol, byte[] additionalData, Action accept, Action reject) + public async Task OnAuthorizationRequest(string key, string name, byte[] additionalData, Action accept, Action reject) { if (key == _configuration.Key) { diff --git a/Ragon.SimpleServer/config.json b/Ragon.SimpleServer/config.json index a373dce..10e516a 100755 --- a/Ragon.SimpleServer/config.json +++ b/Ragon.SimpleServer/config.json @@ -1,5 +1,6 @@ { "key": "defaultkey", + "protocol": "1.0.0", "statisticsInterval": 5, "sendRate": 30, "port": 4444, diff --git a/Ragon/Sources/Authorization/AuthorizationManager.cs b/Ragon/Sources/Authorization/AuthorizationManager.cs index 4dcf481..baf5ac5 100644 --- a/Ragon/Sources/Authorization/AuthorizationManager.cs +++ b/Ragon/Sources/Authorization/AuthorizationManager.cs @@ -25,11 +25,11 @@ public class AuthorizationManager : IAuthorizationManager _playersByPeers = new Dictionary(); } - public void OnAuthorization(uint peerId, string key, string name, byte protocol) + public void OnAuthorization(uint peerId, string key, string name) { var dispatcher = _gameThread.ThreadDispatcher; - _provider.OnAuthorizationRequest(key, name, protocol, Array.Empty(), + _provider.OnAuthorizationRequest(key, name, Array.Empty(), (playerId, playerName) => { dispatcher.Dispatch(() => Accepted(peerId, playerId, playerName)); }, (errorCode) => { dispatcher.Dispatch(() => Rejected(peerId, errorCode)); }); } diff --git a/Ragon/Sources/Authorization/IAuthorizationProvider.cs b/Ragon/Sources/Authorization/IAuthorizationProvider.cs index bd3d3d3..be8967a 100644 --- a/Ragon/Sources/Authorization/IAuthorizationProvider.cs +++ b/Ragon/Sources/Authorization/IAuthorizationProvider.cs @@ -5,5 +5,5 @@ namespace Ragon.Core; public interface IAuthorizationProvider { - Task OnAuthorizationRequest(string key, string playerName, byte protocol, byte[] additionalData, Action Accept, Action Reject); + Task OnAuthorizationRequest(string key, string playerName, byte[] additionalData, Action Accept, Action Reject); } \ No newline at end of file diff --git a/Ragon/Sources/Configuration/Configuration.cs b/Ragon/Sources/Configuration/Configuration.cs index a8e2e4c..890fd03 100755 --- a/Ragon/Sources/Configuration/Configuration.cs +++ b/Ragon/Sources/Configuration/Configuration.cs @@ -6,6 +6,7 @@ namespace Ragon.Core public struct Configuration { public string Key; + public string Protocol; public int StatisticsInterval; public ushort SendRate; public ushort Port; diff --git a/Ragon/Sources/Entity/EntityProperty.cs b/Ragon/Sources/Entity/EntityProperty.cs index 5e0dcbd..fe8017f 100644 --- a/Ragon/Sources/Entity/EntityProperty.cs +++ b/Ragon/Sources/Entity/EntityProperty.cs @@ -1,33 +1,39 @@ using System; +using Ragon.Common; namespace Ragon.Core; public class EntityProperty { - public int Size => _data.Length; - public bool IsDirty => _dirty; - - private bool _dirty; + public int Size { get; set; } + public bool IsDirty { get; private set; } + public bool IsFixed { get; private set; } private byte[] _data; - - public EntityProperty(int size) + + public EntityProperty(int size, bool isFixed) { - _data = new byte[size]; + _data = new byte[512]; + + Size = size; + IsFixed = isFixed; + IsDirty = true; } public ReadOnlySpan Read() { - return _data.AsSpan(); + var dataSpan = _data.AsSpan(); + + return dataSpan.Slice(0, Size); } public void Write(ref ReadOnlySpan src) { src.CopyTo(_data); - _dirty = true; + IsDirty = true; } public void Clear() { - _dirty = false; + IsDirty = false; } } \ No newline at end of file diff --git a/Ragon/Sources/Game/GameRoom.cs b/Ragon/Sources/Game/GameRoom.cs index 8603201..67a77a6 100755 --- a/Ragon/Sources/Game/GameRoom.cs +++ b/Ragon/Sources/Game/GameRoom.cs @@ -12,6 +12,7 @@ namespace Ragon.Core public int PlayersMin { get; private set; } public int PlayersMax { get; private set; } public int PlayersCount => _players.Count; + public int EntitiesCount => _entities.Count; public string Id { get; private set; } public string Map { get; private set; } @@ -116,6 +117,20 @@ namespace Ragon.Core Broadcast(_readyPlayers, sendData); } + if (_allPlayers.Length > 0) + { + var nextOwnerId = _allPlayers[0]; + _owner = nextOwnerId; + var nextOwner = _players[nextOwnerId]; + + _serializer.Clear(); + _serializer.WriteOperation(RagonOperation.OWNERSHIP_CHANGED); + _serializer.WriteString(nextOwner.Id); + + var sendData = _serializer.ToArray(); + Broadcast(_readyPlayers, sendData); + } + _entitiesAll = _entities.Values.ToArray(); } } @@ -129,29 +144,40 @@ namespace Ragon.Core { case RagonOperation.REPLICATE_ENTITY_STATE: { - var entityId = _serializer.ReadUShort(); - if (_entities.TryGetValue(entityId, out var ent)) + var entitiesCount = _serializer.ReadUShort(); + for (var entityIndex = 0; entityIndex < entitiesCount; entityIndex++) { - if (ent.OwnerId != peerId) + var entityId = _serializer.ReadUShort(); + if (_entities.TryGetValue(entityId, out var ent)) { - _logger.Warn($"Not owner can't change properties of object {entityId}"); - return; - } - - var mask = _serializer.ReadLong(); - for (var i = 0; i < ent.Properties.Length; i++) - { - if (((mask >> i) & 1) == 1) + if (ent.OwnerId != peerId) { - var propertyPayload = _serializer.ReadData(ent.Properties[i].Size); - ent.Properties[i].Write(ref propertyPayload); + _logger.Warn($"Not owner can't change properties of object {entityId}"); + return; } + + for (var i = 0; i < ent.Properties.Length; i++) + { + if (_serializer.ReadBool()) + { + var property = ent.Properties[i]; + if (!property.IsFixed) + property.Size = _serializer.ReadUShort(); + + var propertyPayload = _serializer.ReadData(property.Size); + property.Write(ref propertyPayload); + } + } + + if (_entitiesDirtySet.Add(ent)) + _entitiesDirty.Add(ent); + } + else + { + _logger.Error($"Entity with Id {entityId} not found, replication interrupted"); + break; } - - if (_entitiesDirtySet.Add(ent)) - _entitiesDirty.Add(ent); } - break; } case RagonOperation.REPLICATE_ENTITY_EVENT: @@ -182,6 +208,7 @@ namespace Ragon.Core _serializer.WriteByte(evntMode); _serializer.WriteUShort(entityId); _serializer.WriteData(ref payload); + var sendData = _serializer.ToArray(); switch (targetMode) @@ -273,22 +300,18 @@ namespace Ragon.Core break; } - case RagonOperation.CREATE_STATIC_ENTITY: + case RagonOperation.CREATE_SCENE_ENTITY: { var entityType = _serializer.ReadUShort(); var staticId = _serializer.ReadUShort(); var propertiesCount = _serializer.ReadUShort(); - if (propertiesCount > 63) - { - _logger.Warn($"Allowed only 64 properties per entity. EntityType(Static) {entityType}"); - return; - } var entity = new Entity(peerId, entityType, staticId, RagonAuthority.ALL, RagonAuthority.OWNER_ONLY, propertiesCount); for (var i = 0; i < propertiesCount; i++) { + var propertyType = _serializer.ReadBool(); var propertySize = _serializer.ReadUShort(); - entity.Properties[i] = new EntityProperty(propertySize); + entity.Properties[i] = new EntityProperty(propertySize, propertyType); } { @@ -308,7 +331,7 @@ namespace Ragon.Core _plugin.OnEntityCreated(player, entity); _serializer.Clear(); - _serializer.WriteOperation(RagonOperation.CREATE_STATIC_ENTITY); + _serializer.WriteOperation(RagonOperation.CREATE_SCENE_ENTITY); _serializer.WriteUShort(entityType); _serializer.WriteUShort(entity.EntityId); _serializer.WriteUShort(staticId); @@ -328,16 +351,13 @@ namespace Ragon.Core var entityType = _serializer.ReadUShort(); var propertiesCount = _serializer.ReadUShort(); var entity = new Entity(peerId, entityType, 0, RagonAuthority.ALL, RagonAuthority.ALL, propertiesCount); - if (propertiesCount > 63) - { - _logger.Warn($"Allowed only 64 properties per entity. EntityType(Static) {entityType}"); - return; - } - + // _logger.Trace("Created entity with properties: " + propertiesCount); for (var i = 0; i < propertiesCount; i++) { + var propertyType = _serializer.ReadBool(); var propertySize = _serializer.ReadUShort(); - entity.Properties[i] = new EntityProperty(propertySize); + entity.Properties[i] = new EntityProperty(propertySize, propertyType); + // _logger.Trace($"Property: {i} Size: {propertySize} IsFixed: {propertyType}"); } { @@ -461,38 +481,35 @@ namespace Ragon.Core _serializer.Clear(); _serializer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE); _serializer.WriteUShort((ushort) _entitiesDirty.Count); + for (var entityIndex = 0; entityIndex < _entitiesDirty.Count; entityIndex++) { var entity = _entitiesDirty[entityIndex]; - var mask = 0L; - _serializer.WriteUShort(entity.EntityId); - - var offset = _serializer.Lenght; - _serializer.WriteLong(mask); - + for (int propertyIndex = 0; propertyIndex < entity.Properties.Length; propertyIndex++) { var property = entity.Properties[propertyIndex]; if (property.IsDirty) { - mask |= (uint) (1 << propertyIndex); - + _serializer.WriteBool(true); var span = _serializer.GetWritableData(property.Size); var data = property.Read(); data.CopyTo(span); property.Clear(); } + else + { + _serializer.WriteBool(false); + } } - - _serializer.WriteLong(mask, offset); } _entitiesDirty.Clear(); _entitiesDirtySet.Clear(); var sendData = _serializer.ToArray(); - Broadcast(_readyPlayers, sendData, DeliveryType.Unreliable); + Broadcast(_readyPlayers, sendData); } } diff --git a/Ragon/Sources/Game/GameThread.cs b/Ragon/Sources/Game/GameThread.cs index 1a5c3fb..3c32afb 100755 --- a/Ragon/Sources/Game/GameThread.cs +++ b/Ragon/Sources/Game/GameThread.cs @@ -1,5 +1,7 @@ using System; using System.Diagnostics; +using System.Linq; +using System.Security.Cryptography; using System.Threading; using Ragon.Common; using ENet; @@ -12,10 +14,14 @@ namespace Ragon.Core private readonly RoomManager _roomManager; private readonly Thread _thread; private readonly Stopwatch _gameLoopTimer; + private readonly Stopwatch _statisticsTimer; + + private readonly Stopwatch _serverTimer; + private readonly Stopwatch _logicTimer; + private readonly Lobby _lobby; private readonly ILogger _logger = LogManager.GetCurrentClassLogger(); private readonly float _deltaTime = 0.0f; - private readonly Stopwatch _statisticsTimer; private readonly Configuration _configuration; private readonly IDispatcherInternal _dispatcherInternal; @@ -40,6 +46,8 @@ namespace Ragon.Core _gameLoopTimer = new Stopwatch(); _statisticsTimer = new Stopwatch(); + _serverTimer = new Stopwatch(); + _logicTimer = new Stopwatch(); _thread = new Thread(Execute); _thread.Name = "Game Thread"; @@ -48,10 +56,30 @@ namespace Ragon.Core public void Start() { - Server.Start(_configuration.Port, _configuration.MaxConnections); + var strings = _configuration.Protocol.Split("."); + if (strings.Length < 3) + { + _logger.Error("Wrong protocol passed to connect method"); + return; + } + var parts = new uint[] {0, 0, 0}; + for (int i = 0; i < parts.Length; i++) + { + if (!uint.TryParse(strings[i], out var v)) + { + _logger.Error("Wrong protocol"); + return; + } + parts[i] = v; + } + + uint encoded = (parts[0] << 16) | (parts[1] << 8) | parts[2]; + Server.Start(_configuration.Port, _configuration.MaxConnections, encoded); _gameLoopTimer.Start(); _statisticsTimer.Start(); + _logicTimer.Start(); + _serverTimer.Start(); _thread.Start(); } @@ -68,6 +96,9 @@ namespace Ragon.Core { while (true) { + _logicTimer.Restart(); + _serverTimer.Restart(); + Server.Process(); _dispatcherInternal.Process(); @@ -82,7 +113,10 @@ namespace Ragon.Core if (_statisticsTimer.Elapsed.Seconds > _configuration.StatisticsInterval && _roomManager.RoomsBySocket.Count > 0) { - _logger.Trace($"Rooms: {_roomManager.Rooms.Count} Clients: {_roomManager.RoomsBySocket.Count}"); + var rooms = _roomManager.Rooms.Count; + var clients = _roomManager.RoomsBySocket.Count; + var entities = _roomManager.Rooms.Select(r => r.EntitiesCount).Sum(); + _logger.Trace($"Rooms: {rooms} Clients: {clients} Entities: {entities}"); _statisticsTimer.Restart(); } } diff --git a/Ragon/Sources/Lobby/Lobby.cs b/Ragon/Sources/Lobby/Lobby.cs index d9d8d84..a297ab2 100644 --- a/Ragon/Sources/Lobby/Lobby.cs +++ b/Ragon/Sources/Lobby/Lobby.cs @@ -32,8 +32,7 @@ public class Lobby : ILobby { var key = _serializer.ReadString(); var playerName = _serializer.ReadString(); - var protocol = _serializer.ReadByte(); - _authorizationManager.OnAuthorization(peerId, key, playerName, protocol); + _authorizationManager.OnAuthorization(peerId, key, playerName); return; } diff --git a/Ragon/Sources/Server/ENet/ENetServer.cs b/Ragon/Sources/Server/ENet/ENetServer.cs index b024a71..3023232 100755 --- a/Ragon/Sources/Server/ENet/ENetServer.cs +++ b/Ragon/Sources/Server/ENet/ENetServer.cs @@ -1,46 +1,42 @@ using System; +using System.Diagnostics; +using System.Timers; using ENet; using NLog; namespace Ragon.Core { - public enum Status - { - Stopped, - Listening, - Disconnecting, - Connecting, - Assigning, - Connected - } - public class ENetServer : ISocketServer { - public Status Status { get; private set; } - private ILogger _logger = LogManager.GetCurrentClassLogger(); private Host _host; + private uint _protocol; private Address _address; private Event _netEvent; private Peer[] _peers; private IHandler _handler; - + private Stopwatch _timer; + public ENetServer(IHandler handler) { _handler = handler; + _timer = Stopwatch.StartNew(); + _peers = Array.Empty(); + _host = new Host(); } - public void Start(ushort port, int connections) + public void Start(ushort port, int connections, uint protocol) { _address = default; _address.Port = port; _peers = new Peer[connections]; - - _host = new Host(); + _protocol = protocol; _host.Create(_address, connections, 2, 0, 0, 1024 * 1024); - - Status = Status.Listening; + + + var protocolDecoded = (protocol >> 16 & 0xFF) + "." + (protocol >> 8 & 0xFF) + "." + (protocol & 0xFF); _logger.Info($"Network listening on {port}"); + _logger.Info($"Protocol: {protocolDecoded}"); } public void Broadcast(uint[] peersIds, byte[] data, DeliveryType type) @@ -57,14 +53,14 @@ namespace Ragon.Core else if (type == DeliveryType.Unreliable) { channel = 1; - packetFlags = PacketFlags.None; + packetFlags = PacketFlags.UnreliableFragmented; } newPacket.Create(data, data.Length, packetFlags); foreach (var peerId in peersIds) _peers[peerId].Send(channel, ref newPacket); } - + public void Send(uint peerId, byte[] data, DeliveryType type) { var newPacket = new Packet(); @@ -107,11 +103,17 @@ namespace Ragon.Core switch (_netEvent.Type) { case EventType.None: - Console.WriteLine("None event"); + { + _logger.Trace("None event"); break; - + } case EventType.Connect: { + // if (IsValidProtocol(_netEvent.Data)) + // { + // _logger.Warn("Mismatched protocol, close connection"); + // break; + // } _peers[_netEvent.Peer.ID] = _netEvent.Peer; _handler.OnEvent(_netEvent); break; @@ -140,5 +142,10 @@ namespace Ragon.Core { _host?.Dispose(); } + + private bool IsValidProtocol(uint protocol) + { + return protocol == _protocol; + } } } \ No newline at end of file diff --git a/Ragon/Sources/Server/ISocketServer.cs b/Ragon/Sources/Server/ISocketServer.cs index 09c6b9a..e4b27f2 100644 --- a/Ragon/Sources/Server/ISocketServer.cs +++ b/Ragon/Sources/Server/ISocketServer.cs @@ -2,7 +2,7 @@ namespace Ragon.Core; public interface ISocketServer { - public void Start(ushort port, int connections); + public void Start(ushort port, int connections, uint protocol); public void Process(); public void Stop(); public void Send(uint peerId, byte[] data, DeliveryType type);