commit dabf1f3ba9731e634e88b84fbd347f2d671db02f Author: tolelom <98kimsungmin@naver.com> Date: Thu Feb 26 17:52:48 2026 +0900 first commit diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..48dbc6e --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,15 @@ +{ + "permissions": { + "allow": [ + "Bash(mkdir:*)", + "Bash(protoc:*)", + "Bash(go install:*)", + "Bash(winget install:*)", + "Bash(export:*)", + "Bash(go build:*)", + "Bash(go test:*)", + "Bash(go:*)", + "Bash(\"C:/Users/SSAFY/AppData/Local/Microsoft/WinGet/Packages/Google.Protobuf_Microsoft.Winget.Source_8wekyb3d8bbwe/bin/protoc.exe\":*)" + ] + } +} diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..9879198 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# 디폴트 무시된 파일 +/shelf/ +/workspace.xml +# 쿼리 파일을 포함한 무시된 디폴트 폴더 +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# 에디터 기반 HTTP 클라이언트 요청 +/httpRequests/ diff --git a/.idea/a301_game_server.iml b/.idea/a301_game_server.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/a301_game_server.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/go.imports.xml b/.idea/go.imports.xml new file mode 100644 index 0000000..d7202f0 --- /dev/null +++ b/.idea/go.imports.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..649473c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..91445dd --- /dev/null +++ b/Makefile @@ -0,0 +1,60 @@ +PROTOC := C:/Users/SSAFY/AppData/Local/Microsoft/WinGet/Packages/Google.Protobuf_Microsoft.Winget.Source_8wekyb3d8bbwe/bin/protoc.exe +GOPATH_BIN := $(shell go env GOPATH)/bin + +.PHONY: build run proto proto-unity proto-all clean test testclient + +# Build the server binary. +build: + go build -o bin/server.exe ./cmd/server + +# Run the server. +run: + go run ./cmd/server -config config/config.yaml + +# Generate Go server code from protobuf. +proto: + PATH="$(GOPATH_BIN):$(PATH)" $(PROTOC) \ + --go_out=proto/gen/pb \ + --go_opt=paths=source_relative \ + --proto_path=proto \ + proto/messages.proto + +# Generate C# Unity client code from protobuf. +proto-unity: + $(PROTOC) \ + --csharp_out=./unity/Assets/Scripts/Proto \ + --proto_path=./proto \ + proto/messages.proto + +# Generate both Go and C# at once. +proto-all: proto proto-unity + +# Clean build artifacts. +clean: + rm -rf bin/ + +# Run tests. +test: + go test ./... -v -race + +# Run with AOI disabled for comparison. +run-no-aoi: + AOI_ENABLED=false go run ./cmd/server -config config/config.yaml + +# Run the WebSocket test client (server must be running). +testclient: + go run ./cmd/testclient + +# Run test client with specific scenario (auth|move|combat|metrics|all). +testclient-auth: + go run ./cmd/testclient -scenario auth + +testclient-combat: + go run ./cmd/testclient -scenario combat + +testclient-metrics: + go run ./cmd/testclient -scenario metrics + +# Load test: 10 clients for 15 seconds. +loadtest: + go run ./cmd/testclient -clients 10 -duration 15s diff --git a/UNITY_INTEGRATION.md b/UNITY_INTEGRATION.md new file mode 100644 index 0000000..36823b8 --- /dev/null +++ b/UNITY_INTEGRATION.md @@ -0,0 +1,865 @@ +# Unity 클라이언트 연동 가이드 + +## 목차 + +1. [패키지 설치](#1-패키지-설치) +2. [Protobuf C# 코드 생성](#2-protobuf-c-코드-생성) +3. [네트워크 레이어 구현](#3-네트워크-레이어-구현) +4. [메시지 타입 정의](#4-메시지-타입-정의) +5. [인증 흐름](#5-인증-흐름) +6. [이동 동기화](#6-이동-동기화) +7. [엔티티 관리 (AOI)](#7-엔티티-관리-aoi) +8. [전투 시스템](#8-전투-시스템) +9. [존 전환](#9-존-전환) +10. [디버그 도구](#10-디버그-도구) +11. [전체 GameManager 예시](#11-전체-gamemanager-예시) + +--- + +## 1. 패키지 설치 + +### Unity Package Manager에서 설치 + +**Window → Package Manager → Add package by name** + +``` +com.unity.nuget.newtonsoft-json +``` + +### NuGet 또는 DLL 직접 추가 + +아래 두 패키지가 필요합니다. + +| 패키지 | 용도 | +|--------|------| +| `NativeWebSocket` | WebSocket 통신 | +| `Google.Protobuf` | 메시지 직렬화 | + +**NativeWebSocket** (무료, WebGL 지원) +- [https://github.com/endel/NativeWebSocket](https://github.com/endel/NativeWebSocket) +- `Assets/Plugins/` 폴더에 복사 + +**Google.Protobuf** +- NuGet에서 `Google.Protobuf` 다운로드 +- `Google.Protobuf.dll`을 `Assets/Plugins/` 폴더에 복사 + +--- + +## 2. Protobuf C# 코드 생성 + +서버의 `proto/messages.proto` 파일로부터 C# 코드를 생성합니다. + +### protoc 설치 (Windows) + +``` +winget install Google.Protobuf +``` + +### C# 코드 생성 + +프로젝트 루트에서 실행: + +```bash +protoc --csharp_out=./unity/Assets/Scripts/Proto \ + --proto_path=./proto \ + proto/messages.proto +``` + +생성된 `Messages.cs`를 Unity 프로젝트의 `Assets/Scripts/Proto/` 폴더에 배치합니다. + +--- + +## 3. 네트워크 레이어 구현 + +### 와이어 프로토콜 + +서버와의 통신 형식은 다음과 같습니다. + +``` +[2바이트: 메시지 타입 ID (Big Endian)] [Protobuf 직렬화 페이로드] +``` + +### NetworkClient.cs + +```csharp +using System; +using System.Collections.Generic; +using UnityEngine; +using NativeWebSocket; +using Google.Protobuf; + +public class NetworkClient : MonoBehaviour +{ + public static NetworkClient Instance { get; private set; } + + [Header("Server")] + [SerializeField] private string serverUrl = "ws://localhost:8080/ws"; + + private WebSocket _ws; + private readonly Queue _mainThreadQueue = new(); + + // 메시지 핸들러 테이블 + private readonly Dictionary> _handlers = new(); + + void Awake() + { + if (Instance != null) { Destroy(gameObject); return; } + Instance = this; + DontDestroyOnLoad(gameObject); + RegisterHandlers(); + } + + void Update() + { +#if !UNITY_WEBGL || UNITY_EDITOR + _ws?.DispatchMessageQueue(); +#endif + // 메인 스레드에서 Unity API 호출 + while (_mainThreadQueue.Count > 0) + _mainThreadQueue.Dequeue()?.Invoke(); + } + + public async void Connect() + { + _ws = new WebSocket(serverUrl); + + _ws.OnOpen += () => Debug.Log("[Net] Connected"); + _ws.OnClose += (e) => Debug.Log($"[Net] Disconnected: {e}"); + _ws.OnError += (e) => Debug.LogError($"[Net] Error: {e}"); + _ws.OnMessage += OnRawMessage; + + await _ws.Connect(); + } + + public async void Disconnect() + { + if (_ws != null) + await _ws.Close(); + } + + // ─── 송신 ────────────────────────────────────────────────── + + public async void Send(ushort msgType, IMessage payload) + { + if (_ws?.State != WebSocketState.Open) return; + + byte[] body = payload.ToByteArray(); + byte[] packet = new byte[2 + body.Length]; + + // Big Endian 2바이트 타입 ID + packet[0] = (byte)(msgType >> 8); + packet[1] = (byte)(msgType & 0xFF); + Buffer.BlockCopy(body, 0, packet, 2, body.Length); + + await _ws.Send(packet); + } + + // ─── 수신 ────────────────────────────────────────────────── + + private void OnRawMessage(byte[] data) + { + if (data.Length < 2) return; + + ushort msgType = (ushort)((data[0] << 8) | data[1]); + byte[] payload = new byte[data.Length - 2]; + Buffer.BlockCopy(data, 2, payload, 0, payload.Length); + + _mainThreadQueue.Enqueue(() => + { + if (_handlers.TryGetValue(msgType, out var handler)) + handler(payload); + else + Debug.LogWarning($"[Net] Unhandled message type: 0x{msgType:X4}"); + }); + } + + public void Register(ushort msgType, Action handler) + { + _handlers[msgType] = handler; + } + + // 핸들러 등록은 GameManager에서 수행 (아래 참조) + private void RegisterHandlers() { } +} +``` + +--- + +## 4. 메시지 타입 정의 + +```csharp +public static class MsgType +{ + // Auth + public const ushort LoginRequest = 0x0001; + public const ushort LoginResponse = 0x0002; + public const ushort EnterWorldRequest = 0x0003; + public const ushort EnterWorldResponse = 0x0004; + + // Movement + public const ushort MoveRequest = 0x0010; + public const ushort StateUpdate = 0x0011; + public const ushort SpawnEntity = 0x0012; + public const ushort DespawnEntity = 0x0013; + public const ushort ZoneTransferNotify = 0x0014; + + // System + public const ushort Ping = 0x0020; + public const ushort Pong = 0x0021; + + // Combat + public const ushort UseSkillRequest = 0x0040; + public const ushort UseSkillResponse = 0x0041; + public const ushort CombatEvent = 0x0042; + public const ushort BuffApplied = 0x0043; + public const ushort BuffRemoved = 0x0044; + public const ushort RespawnRequest = 0x0045; + public const ushort RespawnResponse = 0x0046; + + // Debug + public const ushort AOIToggleRequest = 0x0030; + public const ushort AOIToggleResponse = 0x0031; + public const ushort MetricsRequest = 0x0032; + public const ushort ServerMetrics = 0x0033; +} +``` + +--- + +## 5. 인증 흐름 + +``` +클라이언트 서버 + │ │ + │── LoginRequest ───────────────→│ username + password + │← LoginResponse ────────────────│ success, session_token, player_id + │ │ + │── EnterWorldRequest ──────────→│ session_token + │← EnterWorldResponse ───────────│ self(EntityState), zone_id + │ │ + │ [게임 진행] │ +``` + +### AuthManager.cs + +```csharp +public class AuthManager : MonoBehaviour +{ + public static AuthManager Instance { get; private set; } + + public string SessionToken { get; private set; } + public ulong PlayerId { get; private set; } + + public event Action OnLoginResult; + public event Action OnEnterWorldResult; + + void Awake() + { + Instance = this; + NetworkClient.Instance.Register(MsgType.LoginResponse, OnLoginResponse); + NetworkClient.Instance.Register(MsgType.EnterWorldResponse, OnEnterWorldResponse); + } + + public void Login(string username, string password) + { + NetworkClient.Instance.Send(MsgType.LoginRequest, new LoginRequest + { + Username = username, + Password = password + }); + } + + public void EnterWorld() + { + NetworkClient.Instance.Send(MsgType.EnterWorldRequest, new EnterWorldRequest + { + SessionToken = SessionToken + }); + } + + private void OnLoginResponse(byte[] data) + { + var resp = LoginResponse.Parser.ParseFrom(data); + if (resp.Success) + { + SessionToken = resp.SessionToken; + PlayerId = resp.PlayerId; + } + OnLoginResult?.Invoke(resp.Success, resp.ErrorMessage); + } + + private void OnEnterWorldResponse(byte[] data) + { + var resp = EnterWorldResponse.Parser.ParseFrom(data); + if (resp.Success) + OnEnterWorldResult?.Invoke(resp.Self); + } +} +``` + +--- + +## 6. 이동 동기화 + +### 전략: 클라이언트 예측 + 서버 권위 + +- 클라이언트는 **로컬에서 즉시 이동**을 적용합니다 (반응성 유지). +- 매 50ms(서버 틱)마다 현재 위치/속도를 서버에 전송합니다. +- 서버에서 받은 `StateUpdate`로 다른 엔티티의 위치를 보간합니다. + +### MovementSender.cs (로컬 플레이어) + +```csharp +public class MovementSender : MonoBehaviour +{ + [SerializeField] private float sendInterval = 0.05f; // 50ms = 20 tick/s + + private CharacterController _controller; + private float _timer; + private Vector3 _lastSentPos; + + void Awake() => _controller = GetComponent(); + + void Update() + { + // 로컬 이동 입력 처리 (CharacterController 등) + HandleInput(); + + _timer += Time.deltaTime; + if (_timer >= sendInterval) + { + _timer = 0f; + SendMoveIfChanged(); + } + } + + void HandleInput() + { + float h = Input.GetAxis("Horizontal"); + float v = Input.GetAxis("Vertical"); + Vector3 dir = new Vector3(h, 0, v).normalized; + _controller.Move(dir * 5f * Time.deltaTime); + } + + void SendMoveIfChanged() + { + if (Vector3.Distance(transform.position, _lastSentPos) < 0.01f) return; + _lastSentPos = transform.position; + + var vel = _controller.velocity; + NetworkClient.Instance.Send(MsgType.MoveRequest, new MoveRequest + { + Position = ToProtoVec3(transform.position), + Rotation = transform.eulerAngles.y, + Velocity = ToProtoVec3(vel) + }); + } + + static Vector3Proto ToProtoVec3(Vector3 v) => new() { X = v.x, Y = v.y, Z = v.z }; +} +``` + +### RemoteEntityView.cs (다른 플레이어/몬스터) + +```csharp +// 서버에서 받은 위치로 부드럽게 보간 +public class RemoteEntityView : MonoBehaviour +{ + private Vector3 _targetPos; + private float _targetRot; + private const float LerpSpeed = 15f; + + public void ApplyState(EntityState state) + { + _targetPos = new Vector3(state.Position.X, state.Position.Y, state.Position.Z); + _targetRot = state.Rotation; + } + + void Update() + { + transform.position = Vector3.Lerp(transform.position, _targetPos, + LerpSpeed * Time.deltaTime); + transform.rotation = Quaternion.Lerp(transform.rotation, + Quaternion.Euler(0, _targetRot, 0), + LerpSpeed * Time.deltaTime); + } +} +``` + +--- + +## 7. 엔티티 관리 (AOI) + +서버 AOI가 관리하는 엔티티 생성/삭제를 클라이언트에서 처리합니다. + +### EntityManager.cs + +```csharp +public class EntityManager : MonoBehaviour +{ + public static EntityManager Instance { get; private set; } + + [SerializeField] private GameObject playerPrefab; + [SerializeField] private GameObject mobPrefab; + + private readonly Dictionary _entities = new(); + public ulong LocalPlayerId { get; set; } + + void Awake() + { + Instance = this; + NetworkClient.Instance.Register(MsgType.StateUpdate, OnStateUpdate); + NetworkClient.Instance.Register(MsgType.SpawnEntity, OnSpawnEntity); + NetworkClient.Instance.Register(MsgType.DespawnEntity, OnDespawnEntity); + } + + // ─── 수신 핸들러 ─────────────────────────────────────────── + + private void OnStateUpdate(byte[] data) + { + var update = StateUpdate.Parser.ParseFrom(data); + foreach (var state in update.Entities) + UpdateEntity(state); + } + + private void OnSpawnEntity(byte[] data) + { + var msg = SpawnEntity_.Parser.ParseFrom(data); // 네임스페이스 주의 + SpawnEntity(msg.Entity); + } + + private void OnDespawnEntity(byte[] data) + { + var msg = DespawnEntity_.Parser.ParseFrom(data); + DespawnEntity(msg.EntityId); + } + + // ─── 엔티티 수명 관리 ───────────────────────────────────── + + void SpawnEntity(EntityState state) + { + if (state.EntityId == LocalPlayerId) return; + if (_entities.ContainsKey(state.EntityId)) return; + + var prefab = state.EntityType == EntityType.EntityTypeMob ? mobPrefab : playerPrefab; + var go = Instantiate(prefab, + new Vector3(state.Position.X, state.Position.Y, state.Position.Z), + Quaternion.identity); + go.name = $"{state.Name}_{state.EntityId}"; + go.GetComponent()?.ApplyState(state); + go.GetComponent()?.Setup(state.Name, state.Hp, state.MaxHp); + + _entities[state.EntityId] = go; + } + + void UpdateEntity(EntityState state) + { + if (state.EntityId == LocalPlayerId) return; + if (!_entities.TryGetValue(state.EntityId, out var go)) + { + SpawnEntity(state); + return; + } + go.GetComponent()?.ApplyState(state); + go.GetComponent()?.UpdateHP(state.Hp, state.MaxHp); + } + + void DespawnEntity(ulong entityId) + { + if (_entities.TryGetValue(entityId, out var go)) + { + Destroy(go); + _entities.Remove(entityId); + } + } + + public void ClearAll() + { + foreach (var go in _entities.Values) + if (go != null) Destroy(go); + _entities.Clear(); + } +} +``` + +--- + +## 8. 전투 시스템 + +### CombatManager.cs + +```csharp +public class CombatManager : MonoBehaviour +{ + public static CombatManager Instance { get; private set; } + + public event Action OnCombatEvent; + public event Action OnBuffApplied; + public event Action OnBuffRemoved; + + void Awake() + { + Instance = this; + NetworkClient.Instance.Register(MsgType.CombatEvent, OnCombatEventMsg); + NetworkClient.Instance.Register(MsgType.BuffApplied, OnBuffAppliedMsg); + NetworkClient.Instance.Register(MsgType.BuffRemoved, OnBuffRemovedMsg); + NetworkClient.Instance.Register(MsgType.UseSkillResponse, OnUseSkillResponse); + NetworkClient.Instance.Register(MsgType.RespawnResponse, OnRespawnResponse); + } + + // ─── 스킬 사용 ──────────────────────────────────────────── + + // 단일 타겟 (Basic Attack, Fireball, Poison 등) + public void UseSkill(uint skillId, ulong targetEntityId) + { + NetworkClient.Instance.Send(MsgType.UseSkillRequest, new UseSkillRequest + { + SkillId = skillId, + TargetId = targetEntityId + }); + } + + // AoE 지면 타겟 (Flame Strike 등) + public void UseSkillAoE(uint skillId, Vector3 groundPos) + { + NetworkClient.Instance.Send(MsgType.UseSkillRequest, new UseSkillRequest + { + SkillId = skillId, + TargetPos = new Vector3Proto { X = groundPos.x, Y = groundPos.y, Z = groundPos.z } + }); + } + + // 셀프 타겟 (Heal, Power Up 등) + public void UseSkillSelf(uint skillId) + { + NetworkClient.Instance.Send(MsgType.UseSkillRequest, new UseSkillRequest + { + SkillId = skillId + }); + } + + // 리스폰 요청 + public void RequestRespawn() + { + NetworkClient.Instance.Send(MsgType.RespawnRequest, new RespawnRequest()); + } + + // ─── 수신 핸들러 ────────────────────────────────────────── + + private void OnCombatEventMsg(byte[] data) + { + var evt = CombatEvent.Parser.ParseFrom(data); + OnCombatEvent?.Invoke(evt); + + switch (evt.EventType) + { + case CombatEventType.CombatEventDamage: + ShowDamageNumber(evt.TargetId, evt.Damage, evt.IsCritical); + UpdateEntityHP(evt.TargetId, evt.TargetHp, evt.TargetMaxHp); + if (evt.TargetDied) + OnEntityDied(evt.TargetId); + break; + + case CombatEventType.CombatEventHeal: + ShowHealNumber(evt.TargetId, evt.Heal); + UpdateEntityHP(evt.TargetId, evt.TargetHp, evt.TargetMaxHp); + break; + + case CombatEventType.CombatEventDeath: + OnEntityDied(evt.TargetId); + break; + + case CombatEventType.CombatEventRespawn: + OnEntityRespawned(evt.TargetId, evt.TargetHp, evt.TargetMaxHp); + break; + } + } + + private void OnBuffAppliedMsg(byte[] data) + { + var buff = BuffApplied.Parser.ParseFrom(data); + OnBuffApplied?.Invoke(buff); + // UI: 버프 아이콘 추가 + BuffUI.Instance?.AddBuff(buff.TargetId, buff.BuffId, buff.BuffName, + buff.Duration, buff.IsDebuff); + } + + private void OnBuffRemovedMsg(byte[] data) + { + var buff = BuffRemoved.Parser.ParseFrom(data); + OnBuffRemoved?.Invoke(buff); + BuffUI.Instance?.RemoveBuff(buff.TargetId, buff.BuffId); + } + + private void OnUseSkillResponse(byte[] data) + { + var resp = UseSkillResponse.Parser.ParseFrom(data); + if (!resp.Success) + UIManager.Instance?.ShowError(resp.ErrorMessage); // "skill on cooldown" 등 + } + + private void OnRespawnResponse(byte[] data) + { + var resp = RespawnResponse.Parser.ParseFrom(data); + // 로컬 플레이어 상태 복구 + LocalPlayer.Instance?.OnRespawned(resp.Self); + } + + // ─── 헬퍼 ───────────────────────────────────────────────── + + void ShowDamageNumber(ulong entityId, int damage, bool isCrit) + { + // 데미지 폰트 팝업 + Debug.Log($"DMG {damage}{(isCrit ? " CRIT!" : "")} on {entityId}"); + } + + void ShowHealNumber(ulong entityId, int heal) + { + Debug.Log($"HEAL +{heal} on {entityId}"); + } + + void UpdateEntityHP(ulong entityId, int hp, int maxHp) + { + // EntityUI HP 바 업데이트 + } + + void OnEntityDied(ulong entityId) + { + if (entityId == AuthManager.Instance.PlayerId) + { + // 로컬 플레이어 사망 → 리스폰 UI 표시 + UIManager.Instance?.ShowRespawnPanel(); + } + // 사망 애니메이션 등 + } + + void OnEntityRespawned(ulong entityId, int hp, int maxHp) { } +} +``` + +### 스킬 ID 참조 + +| ID | 이름 | 타입 | 설명 | +|----|------|------|------| +| 1 | Basic Attack | 단일 적 | 근접 물리 공격 | +| 2 | Fireball | 단일 적 | 원거리 마법 | +| 3 | Heal | 자신 | HP 회복 | +| 4 | Flame Strike | 지면 AoE | 범위 화염 공격 | +| 5 | Poison | 단일 적 | 독 DoT (10초) | +| 6 | Power Up | 자신 | STR 버프 (10초) | + +--- + +## 9. 존 전환 + +포탈 위치에 접근하면 서버가 `ZoneTransferNotify`를 보냅니다. + +```csharp +public class ZoneManager : MonoBehaviour +{ + public static ZoneManager Instance { get; private set; } + + public uint CurrentZoneId { get; private set; } + + void Awake() + { + Instance = this; + NetworkClient.Instance.Register(MsgType.ZoneTransferNotify, OnZoneTransfer); + } + + private void OnZoneTransfer(byte[] data) + { + var notify = ZoneTransferNotify.Parser.ParseFrom(data); + + // 1. 현재 존의 모든 엔티티 제거 + EntityManager.Instance.ClearAll(); + + // 2. 로딩 화면 표시 (선택) + UIManager.Instance?.ShowLoading(true); + + // 3. 새 존 정보 적용 + CurrentZoneId = notify.NewZoneId; + + // 4. 로컬 플레이어 위치 이동 + var pos = notify.Self.Position; + LocalPlayer.Instance?.Teleport(new Vector3(pos.X, pos.Y, pos.Z)); + + // 5. 주변 엔티티 스폰 + foreach (var entity in notify.NearbyEntities) + EntityManager.Instance.SpawnEntityPublic(entity); + + UIManager.Instance?.ShowLoading(false); + Debug.Log($"[Zone] Transferred to zone {notify.NewZoneId}"); + } +} +``` + +--- + +## 10. 디버그 도구 + +게임 중 AOI 토글과 서버 메트릭을 확인할 수 있습니다. + +```csharp +public class DebugPanel : MonoBehaviour +{ + void Start() + { + NetworkClient.Instance.Register(MsgType.AOIToggleResponse, OnAOIToggle); + NetworkClient.Instance.Register(MsgType.ServerMetrics, OnMetrics); + } + + // AOI 켜기/끄기 (성능 비교용) + public void ToggleAOI(bool enabled) + { + NetworkClient.Instance.Send(MsgType.AOIToggleRequest, new AOIToggleRequest + { + Enabled = enabled + }); + } + + // 서버 메트릭 요청 + public void RequestMetrics() + { + NetworkClient.Instance.Send(MsgType.MetricsRequest, new MetricsRequest()); + } + + private void OnAOIToggle(byte[] data) + { + var resp = AOIToggleResponse.Parser.ParseFrom(data); + Debug.Log($"[Debug] AOI: {resp.Message}"); + } + + private void OnMetrics(byte[] data) + { + var m = ServerMetrics.Parser.ParseFrom(data); + Debug.Log($"[Metrics] Players={m.OnlinePlayers} " + + $"Entities={m.TotalEntities} " + + $"Tick={m.TickDurationUs}us " + + $"AOI={m.AoiEnabled}"); + } +} +``` + +--- + +## 11. 전체 GameManager 예시 + +모든 시스템을 하나의 씬에서 연결하는 예시입니다. + +```csharp +public class GameManager : MonoBehaviour +{ + public static GameManager Instance { get; private set; } + + [Header("UI")] + [SerializeField] private LoginPanel loginPanel; + [SerializeField] private HUDPanel hudPanel; + + void Awake() => Instance = this; + + void Start() + { + // 1. 서버 접속 + NetworkClient.Instance.Connect(); + + // 2. 인증 이벤트 구독 + AuthManager.Instance.OnLoginResult += HandleLoginResult; + AuthManager.Instance.OnEnterWorldResult += HandleEnterWorld; + + // 3. 로그인 UI 표시 + loginPanel.gameObject.SetActive(true); + } + + // 로그인 버튼 → AuthManager.Login() 호출 + public void OnLoginButtonClick(string user, string pass) + { + AuthManager.Instance.Login(user, pass); + } + + private void HandleLoginResult(bool success, string error) + { + if (!success) { loginPanel.ShowError(error); return; } + loginPanel.gameObject.SetActive(false); + AuthManager.Instance.EnterWorld(); + } + + private void HandleEnterWorld(EntityState self) + { + // 로컬 플레이어 생성 + EntityManager.Instance.LocalPlayerId = self.EntityId; + LocalPlayer.Instance?.Initialize(self); + + // HUD 표시 + hudPanel.gameObject.SetActive(true); + hudPanel.UpdateHP(self.Hp, self.MaxHp); + } + + void OnApplicationQuit() + { + NetworkClient.Instance.Disconnect(); + } +} +``` + +--- + +## 흐름 다이어그램 + +``` +게임 시작 + │ + ▼ +NetworkClient.Connect() ← ws://서버IP:8080/ws + │ + ▼ +AuthManager.Login() ← LoginRequest + │ LoginResponse (session_token) + ▼ +AuthManager.EnterWorld() ← EnterWorldRequest + │ EnterWorldResponse (self, zone_id) + ▼ +EntityManager 초기화 ← SpawnEntity (주변 플레이어/몬스터) + │ + ▼ +게임 루프 (매 프레임) + ├─ MovementSender → MoveRequest (50ms마다) + ├─ CombatManager → UseSkillRequest (스킬 버튼) + │ ← StateUpdate (20tick/s, 주변 엔티티 위치) + │ ← CombatEvent (피해/힐/사망) + │ ← BuffApplied / BuffRemoved + │ ← SpawnEntity / DespawnEntity (AOI 진입/이탈) + └─ ZoneManager ← ZoneTransferNotify (포탈 진입 시) +``` + +--- + +## 주의사항 + +### Protobuf 네임스페이스 +생성된 C# 클래스 이름이 Unity 내장 타입과 충돌할 수 있습니다. + +```csharp +// Vector3는 UnityEngine.Vector3와 충돌 → proto 타입 명시 +var v = new Proto.Vector3 { X = 1, Y = 0, Z = 0 }; +// 또는 별칭 사용 +using Vector3Proto = Proto.Vector3; +``` + +### WebGL 빌드 +WebGL에서는 `NativeWebSocket`의 JavaScript 백엔드가 사용됩니다. +`_ws.DispatchMessageQueue()`를 `#if !UNITY_WEBGL || UNITY_EDITOR`로 감싸야 합니다. + +### 스레드 안전 +WebSocket 콜백은 백그라운드 스레드에서 호출됩니다. +Unity API (`Instantiate`, `Destroy` 등)는 반드시 **메인 스레드**에서 호출해야 합니다. +위 `NetworkClient`의 `_mainThreadQueue` 패턴을 사용하세요. + +### 핑 측정 +```csharp +long sentTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); +NetworkClient.Instance.Send(MsgType.Ping, new Ping { ClientTime = sentTime }); + +// Pong 수신 시: +// latency = (pong.ServerTime - pong.ClientTime) / 2 (단방향 추정) +``` diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 0000000..e820a50 --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1,75 @@ +package main + +import ( + "context" + "flag" + "os" + "os/signal" + "syscall" + + "a301_game_server/config" + "a301_game_server/internal/db" + "a301_game_server/internal/game" + "a301_game_server/internal/network" + "a301_game_server/pkg/logger" +) + +func main() { + configPath := flag.String("config", "config/config.yaml", "path to configuration file") + flag.Parse() + + cfg, err := config.Load(*configPath) + if err != nil { + panic("failed to load config: " + err.Error()) + } + + if err := logger.Init(cfg.Log.Level); err != nil { + panic("failed to init logger: " + err.Error()) + } + defer logger.Sync() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Connect to PostgreSQL. + dbPool, err := db.NewPool(ctx, &cfg.Database) + if err != nil { + logger.Fatal("failed to connect to database", "error", err) + } + defer dbPool.Close() + + // Run migrations. + if err := dbPool.RunMigrations(ctx); err != nil { + logger.Fatal("failed to run migrations", "error", err) + } + + // Create game server. + gs := game.NewGameServer(cfg, dbPool) + gs.Start() + defer gs.Stop() + + // Create and start network server. + srv := network.NewServer(cfg, gs) + + // Graceful shutdown on SIGINT/SIGTERM. + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + + go func() { + sig := <-sigCh + logger.Info("received signal, shutting down", "signal", sig) + cancel() + }() + + logger.Info("game server starting", + "address", cfg.Server.Address(), + "tickRate", cfg.World.TickRate, + "aoiEnabled", cfg.World.AOI.Enabled, + ) + + if err := srv.Start(ctx); err != nil { + logger.Fatal("server error", "error", err) + } + + logger.Info("server shutdown complete") +} diff --git a/cmd/testclient/main.go b/cmd/testclient/main.go new file mode 100644 index 0000000..c434543 --- /dev/null +++ b/cmd/testclient/main.go @@ -0,0 +1,650 @@ +// testclient: 서버와 WebSocket 연결해서 전체 게임 흐름을 테스트하는 CLI 클라이언트. +// +// 사용법: +// +// go run ./cmd/testclient # 기본 (ws://localhost:8080/ws, user1/pass1) +// go run ./cmd/testclient -url ws://host:8080/ws # 서버 주소 지정 +// go run ./cmd/testclient -user alice -pass secret +// go run ./cmd/testclient -clients 5 # 5명 동시 접속 부하 테스트 +// go run ./cmd/testclient -scenario combat # 전투 시나리오만 실행 +package main + +import ( + "encoding/binary" + "flag" + "fmt" + "log" + "math/rand" + "net/url" + "os" + "os/signal" + "sync" + "syscall" + "time" + + "github.com/gorilla/websocket" + pb "a301_game_server/proto/gen/pb" + "google.golang.org/protobuf/proto" +) + +// ─── 메시지 타입 ID ──────────────────────────────────────────────── +const ( + MsgLoginRequest uint16 = 0x0001 + MsgLoginResponse uint16 = 0x0002 + MsgEnterWorldRequest uint16 = 0x0003 + MsgEnterWorldResponse uint16 = 0x0004 + + MsgMoveRequest uint16 = 0x0010 + MsgStateUpdate uint16 = 0x0011 + MsgSpawnEntity uint16 = 0x0012 + MsgDespawnEntity uint16 = 0x0013 + MsgZoneTransferNotify uint16 = 0x0014 + + MsgPing uint16 = 0x0020 + MsgPong uint16 = 0x0021 + + MsgUseSkillRequest uint16 = 0x0040 + MsgUseSkillResponse uint16 = 0x0041 + MsgCombatEvent uint16 = 0x0042 + MsgBuffApplied uint16 = 0x0043 + MsgBuffRemoved uint16 = 0x0044 + MsgRespawnRequest uint16 = 0x0045 + MsgRespawnResponse uint16 = 0x0046 + + MsgAOIToggleRequest uint16 = 0x0030 + MsgAOIToggleResponse uint16 = 0x0031 + MsgMetricsRequest uint16 = 0x0032 + MsgServerMetrics uint16 = 0x0033 +) + +var msgTypeNames = map[uint16]string{ + MsgLoginRequest: "LoginRequest", MsgLoginResponse: "LoginResponse", + MsgEnterWorldRequest: "EnterWorldRequest", MsgEnterWorldResponse: "EnterWorldResponse", + MsgMoveRequest: "MoveRequest", MsgStateUpdate: "StateUpdate", + MsgSpawnEntity: "SpawnEntity", MsgDespawnEntity: "DespawnEntity", + MsgZoneTransferNotify: "ZoneTransferNotify", + MsgPing: "Ping", MsgPong: "Pong", + MsgUseSkillRequest: "UseSkillRequest", MsgUseSkillResponse: "UseSkillResponse", + MsgCombatEvent: "CombatEvent", MsgBuffApplied: "BuffApplied", + MsgBuffRemoved: "BuffRemoved", + MsgRespawnRequest: "RespawnRequest", MsgRespawnResponse: "RespawnResponse", + MsgAOIToggleRequest: "AOIToggleRequest", MsgAOIToggleResponse: "AOIToggleResponse", + MsgMetricsRequest: "MetricsRequest", MsgServerMetrics: "ServerMetrics", +} + +// ─── 클라이언트 ──────────────────────────────────────────────────── +type Client struct { + id int + username string + password string + conn *websocket.Conn + logger *log.Logger + + sessionToken string + playerID uint64 + zoneID uint32 + + done chan struct{} + recvCh chan recvMsg + + mu sync.Mutex + entities map[uint64]*pb.EntityState +} + +type recvMsg struct { + msgType uint16 + data []byte +} + +func newClient(id int, username, password string) *Client { + return &Client{ + id: id, + username: username, + password: password, + logger: log.New(os.Stdout, fmt.Sprintf("[client-%d] ", id), log.Ltime|log.Lmsgprefix), + done: make(chan struct{}), + recvCh: make(chan recvMsg, 64), + entities: make(map[uint64]*pb.EntityState), + } +} + +func (c *Client) connect(serverURL string) error { + u, err := url.Parse(serverURL) + if err != nil { + return err + } + + conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil) + if err != nil { + return fmt.Errorf("dial failed: %w", err) + } + c.conn = conn + c.logger.Printf("연결 성공: %s", serverURL) + + // 수신 루프 + go c.readLoop() + return nil +} + +func (c *Client) readLoop() { + defer close(c.done) + for { + _, data, err := c.conn.ReadMessage() + if err != nil { + c.logger.Printf("연결 종료: %v", err) + return + } + if len(data) < 2 { + c.logger.Printf("패킷 너무 짧음 (%d bytes)", len(data)) + continue + } + msgType := binary.BigEndian.Uint16(data[:2]) + payload := data[2:] + + // Ping에는 자동으로 Pong 응답 + if msgType == MsgPing { + var ping pb.Ping + if err := proto.Unmarshal(payload, &ping); err == nil { + _ = c.send(MsgPong, &pb.Pong{ClientTime: ping.ClientTime}) + } + continue + } + + select { + case c.recvCh <- recvMsg{msgType, payload}: + default: + c.logger.Printf("recvCh 버퍼 풀 - 메시지 드롭: 0x%04X", msgType) + } + } +} + +func (c *Client) send(msgType uint16, msg proto.Message) error { + body, err := proto.Marshal(msg) + if err != nil { + return err + } + packet := make([]byte, 2+len(body)) + binary.BigEndian.PutUint16(packet[:2], msgType) + copy(packet[2:], body) + return c.conn.WriteMessage(websocket.BinaryMessage, packet) +} + +// waitFor: 특정 메시지 타입이 올 때까지 대기 (timeout 적용) +func (c *Client) waitFor(msgType uint16, timeout time.Duration) ([]byte, error) { + deadline := time.After(timeout) + for { + select { + case msg := <-c.recvCh: + name, ok := msgTypeNames[msg.msgType] + if !ok { + name = fmt.Sprintf("0x%04X", msg.msgType) + } + if msg.msgType == msgType { + c.logger.Printf(" <- %s (받음)", name) + return msg.data, nil + } + // 기다리는 타입이 아닌 것은 즉시 처리 + c.handleAsync(msg) + case <-deadline: + return nil, fmt.Errorf("타임아웃: 0x%04X 메시지 대기 중 %v 초과", msgType, timeout) + case <-c.done: + return nil, fmt.Errorf("연결 끊김") + } + } +} + +// handleAsync: waitFor 중 받은 다른 메시지를 처리 +func (c *Client) handleAsync(msg recvMsg) { + name, ok := msgTypeNames[msg.msgType] + if !ok { + name = fmt.Sprintf("0x%04X", msg.msgType) + } + + switch msg.msgType { + case MsgStateUpdate: + var upd pb.StateUpdate + if err := proto.Unmarshal(msg.data, &upd); err == nil { + c.mu.Lock() + for _, e := range upd.Entities { + c.entities[e.EntityId] = e + } + c.mu.Unlock() + } + case MsgSpawnEntity: + var sp pb.SpawnEntity + if err := proto.Unmarshal(msg.data, &sp); err == nil { + c.mu.Lock() + c.entities[sp.Entity.EntityId] = sp.Entity + c.mu.Unlock() + c.logger.Printf(" <- SpawnEntity: [%d] %s (HP:%d/%d) @ (%.1f,%.1f)", + sp.Entity.EntityId, sp.Entity.Name, sp.Entity.Hp, sp.Entity.MaxHp, + sp.Entity.Position.GetX(), sp.Entity.Position.GetZ()) + } + case MsgDespawnEntity: + var dp pb.DespawnEntity + if err := proto.Unmarshal(msg.data, &dp); err == nil { + c.mu.Lock() + delete(c.entities, dp.EntityId) + c.mu.Unlock() + c.logger.Printf(" <- DespawnEntity: [%d]", dp.EntityId) + } + case MsgCombatEvent: + var evt pb.CombatEvent + if err := proto.Unmarshal(msg.data, &evt); err == nil { + switch evt.EventType { + case pb.CombatEventType_COMBAT_EVENT_DAMAGE: + crit := "" + if evt.IsCritical { + crit = " [CRIT]" + } + c.logger.Printf(" <- CombatEvent: [%d] -> [%d] 데미지 %d%s (남은HP: %d/%d)", + evt.CasterId, evt.TargetId, evt.Damage, crit, evt.TargetHp, evt.TargetMaxHp) + case pb.CombatEventType_COMBAT_EVENT_HEAL: + c.logger.Printf(" <- CombatEvent: [%d] 힐 +%d (HP: %d/%d)", + evt.TargetId, evt.Heal, evt.TargetHp, evt.TargetMaxHp) + case pb.CombatEventType_COMBAT_EVENT_DEATH: + c.logger.Printf(" <- CombatEvent: [%d] 사망", evt.TargetId) + case pb.CombatEventType_COMBAT_EVENT_RESPAWN: + c.logger.Printf(" <- CombatEvent: [%d] 리스폰 (HP: %d/%d)", + evt.TargetId, evt.TargetHp, evt.TargetMaxHp) + } + } + case MsgBuffApplied: + var b pb.BuffApplied + if err := proto.Unmarshal(msg.data, &b); err == nil { + debuff := "" + if b.IsDebuff { + debuff = "[디버프]" + } + c.logger.Printf(" <- BuffApplied%s: [%d] %s (%.1fs)", debuff, b.TargetId, b.BuffName, b.Duration) + } + case MsgBuffRemoved: + var b pb.BuffRemoved + if err := proto.Unmarshal(msg.data, &b); err == nil { + c.logger.Printf(" <- BuffRemoved: [%d] buff#%d", b.TargetId, b.BuffId) + } + case MsgZoneTransferNotify: + var z pb.ZoneTransferNotify + if err := proto.Unmarshal(msg.data, &z); err == nil { + c.zoneID = z.NewZoneId + c.logger.Printf(" <- ZoneTransfer: 새 존 %d (주변 엔티티 %d개)", z.NewZoneId, len(z.NearbyEntities)) + } + default: + c.logger.Printf(" <- %s (비동기 수신)", name) + } +} + +// ─── 시나리오 ────────────────────────────────────────────────────── + +// stepAuth: 로그인 → 월드 입장 +func (c *Client) stepAuth() error { + // 1. 로그인 + c.logger.Printf("→ LoginRequest (user=%s)", c.username) + if err := c.send(MsgLoginRequest, &pb.LoginRequest{ + Username: c.username, + Password: c.password, + }); err != nil { + return err + } + + data, err := c.waitFor(MsgLoginResponse, 5*time.Second) + if err != nil { + return err + } + var resp pb.LoginResponse + if err := proto.Unmarshal(data, &resp); err != nil { + return err + } + if !resp.Success { + return fmt.Errorf("로그인 실패: %s", resp.ErrorMessage) + } + c.sessionToken = resp.SessionToken + c.playerID = resp.PlayerId + c.logger.Printf(" 로그인 성공: playerID=%d, token=%s…", resp.PlayerId, resp.SessionToken[:8]) + + // 2. 월드 입장 + c.logger.Printf("→ EnterWorldRequest") + if err := c.send(MsgEnterWorldRequest, &pb.EnterWorldRequest{ + SessionToken: c.sessionToken, + }); err != nil { + return err + } + + data, err = c.waitFor(MsgEnterWorldResponse, 5*time.Second) + if err != nil { + return err + } + var ewResp pb.EnterWorldResponse + if err := proto.Unmarshal(data, &ewResp); err != nil { + return err + } + if !ewResp.Success { + return fmt.Errorf("월드 입장 실패: %s", ewResp.ErrorMessage) + } + c.zoneID = ewResp.ZoneId + c.logger.Printf(" 월드 입장 성공: zone=%d, pos=(%.1f, %.1f, %.1f)", + ewResp.ZoneId, + ewResp.Self.Position.GetX(), + ewResp.Self.Position.GetY(), + ewResp.Self.Position.GetZ()) + c.logger.Printf(" 플레이어 정보: name=%s, HP=%d/%d, level=%d", + ewResp.Self.Name, ewResp.Self.Hp, ewResp.Self.MaxHp, ewResp.Self.Level) + return nil +} + +// stepMove: 위치 이동 전송 후 StateUpdate 수신 확인 +func (c *Client) stepMove() error { + c.logger.Printf("─── 이동 테스트 ───") + positions := [][2]float32{{5, 5}, {10, 10}, {15, 5}, {10, 0}} + + for _, pos := range positions { + c.logger.Printf("→ MoveRequest (%.1f, 0, %.1f)", pos[0], pos[1]) + if err := c.send(MsgMoveRequest, &pb.MoveRequest{ + Position: &pb.Vector3{X: pos[0], Y: 0, Z: pos[1]}, + Velocity: &pb.Vector3{X: 1, Y: 0, Z: 0}, + Rotation: 0, + }); err != nil { + return err + } + time.Sleep(100 * time.Millisecond) + } + + // StateUpdate 수신 대기 (이동 후 서버 틱 안에 옴) + c.logger.Printf("StateUpdate 대기 중…") + _, err := c.waitFor(MsgStateUpdate, 3*time.Second) + if err != nil { + c.logger.Printf(" 경고: StateUpdate 없음 (혼자 접속 중이면 정상)") + return nil + } + c.mu.Lock() + c.logger.Printf(" 현재 시야 내 엔티티 수: %d", len(c.entities)) + c.mu.Unlock() + return nil +} + +// stepCombat: 주변 몹 타겟팅 후 스킬 사용 +func (c *Client) stepCombat() error { + c.logger.Printf("─── 전투 테스트 ───") + + // 주변 몹 찾기 (SpawnEntity로 받은 것들 중 mob 타입) + var mobID uint64 + c.mu.Lock() + for id, e := range c.entities { + if e.EntityType == pb.EntityType_ENTITY_TYPE_MOB { + mobID = id + c.logger.Printf(" 타겟 몹 발견: [%d] %s (HP:%d/%d)", id, e.Name, e.Hp, e.MaxHp) + break + } + } + c.mu.Unlock() + + if mobID == 0 { + c.logger.Printf(" 주변에 몹이 없음 - 스킬 테스트 스킵") + return nil + } + + skills := []struct { + id uint32 + name string + }{ + {1, "Basic Attack"}, + {2, "Fireball"}, + } + + for _, skill := range skills { + c.logger.Printf("→ UseSkillRequest: %s (skillID=%d) → 타겟 [%d]", skill.name, skill.id, mobID) + if err := c.send(MsgUseSkillRequest, &pb.UseSkillRequest{ + SkillId: skill.id, + TargetId: mobID, + }); err != nil { + return err + } + + data, err := c.waitFor(MsgUseSkillResponse, 3*time.Second) + if err != nil { + return err + } + var resp pb.UseSkillResponse + if err := proto.Unmarshal(data, &resp); err != nil { + return err + } + if resp.Success { + c.logger.Printf(" 스킬 성공") + } else { + c.logger.Printf(" 스킬 실패: %s", resp.ErrorMessage) + } + time.Sleep(200 * time.Millisecond) // 쿨다운 대기 + } + + return nil +} + +// stepHeal: 자신 힐 스킬 +func (c *Client) stepHeal() error { + c.logger.Printf("─── 힐 테스트 ───") + c.logger.Printf("→ UseSkillRequest: Heal (skillID=3, 셀프)") + if err := c.send(MsgUseSkillRequest, &pb.UseSkillRequest{SkillId: 3}); err != nil { + return err + } + data, err := c.waitFor(MsgUseSkillResponse, 3*time.Second) + if err != nil { + return err + } + var resp pb.UseSkillResponse + if err := proto.Unmarshal(data, &resp); err != nil { + return err + } + if resp.Success { + c.logger.Printf(" 힐 성공") + } else { + c.logger.Printf(" 힐 실패: %s", resp.ErrorMessage) + } + return nil +} + +// stepMetrics: 서버 메트릭 요청 +func (c *Client) stepMetrics() error { + c.logger.Printf("─── 서버 메트릭 ───") + c.logger.Printf("→ MetricsRequest") + if err := c.send(MsgMetricsRequest, &pb.MetricsRequest{}); err != nil { + return err + } + data, err := c.waitFor(MsgServerMetrics, 3*time.Second) + if err != nil { + return err + } + var m pb.ServerMetrics + if err := proto.Unmarshal(data, &m); err != nil { + return err + } + c.logger.Printf(" 온라인 플레이어: %d", m.OnlinePlayers) + c.logger.Printf(" 총 엔티티: %d", m.TotalEntities) + c.logger.Printf(" 틱 시간: %d µs", m.TickDurationUs) + c.logger.Printf(" AOI 활성화: %v", m.AoiEnabled) + return nil +} + +// stepAOIToggle: AOI on/off 토글 테스트 +func (c *Client) stepAOIToggle(enabled bool) error { + c.logger.Printf("─── AOI 토글 (enabled=%v) ───", enabled) + if err := c.send(MsgAOIToggleRequest, &pb.AOIToggleRequest{Enabled: enabled}); err != nil { + return err + } + data, err := c.waitFor(MsgAOIToggleResponse, 3*time.Second) + if err != nil { + return err + } + var resp pb.AOIToggleResponse + if err := proto.Unmarshal(data, &resp); err != nil { + return err + } + c.logger.Printf(" AOI 토글 결과: %s", resp.Message) + return nil +} + +// runScenario: 전체 또는 특정 시나리오 실행 +func (c *Client) runScenario(scenario string) { + // 공통: 인증 + if err := c.stepAuth(); err != nil { + c.logger.Printf("[FAIL] 인증: %v", err) + return + } + + switch scenario { + case "auth": + c.logger.Printf("[OK] 인증 시나리오 완료") + + case "move": + if err := c.stepMove(); err != nil { + c.logger.Printf("[FAIL] 이동: %v", err) + return + } + c.logger.Printf("[OK] 이동 시나리오 완료") + + case "combat": + // 이동해서 몹 시야 안으로 + time.Sleep(500 * time.Millisecond) // SpawnEntity 수신 대기 + if err := c.stepCombat(); err != nil { + c.logger.Printf("[FAIL] 전투: %v", err) + return + } + if err := c.stepHeal(); err != nil { + c.logger.Printf("[FAIL] 힐: %v", err) + return + } + c.logger.Printf("[OK] 전투 시나리오 완료") + + case "metrics": + if err := c.stepMetrics(); err != nil { + c.logger.Printf("[FAIL] 메트릭: %v", err) + return + } + if err := c.stepAOIToggle(false); err != nil { + c.logger.Printf("[FAIL] AOI 토글: %v", err) + return + } + if err := c.stepAOIToggle(true); err != nil { + c.logger.Printf("[FAIL] AOI 토글: %v", err) + return + } + c.logger.Printf("[OK] 메트릭 시나리오 완료") + + default: // "all" + time.Sleep(500 * time.Millisecond) // SpawnEntity 수신 대기 + + if err := c.stepMove(); err != nil { + c.logger.Printf("[FAIL] 이동: %v", err) + } + if err := c.stepCombat(); err != nil { + c.logger.Printf("[FAIL] 전투: %v", err) + } + if err := c.stepHeal(); err != nil { + c.logger.Printf("[FAIL] 힐: %v", err) + } + if err := c.stepMetrics(); err != nil { + c.logger.Printf("[FAIL] 메트릭: %v", err) + } + c.logger.Printf("[OK] 전체 시나리오 완료") + } +} + +// ─── 부하 테스트 ────────────────────────────────────────────────── +func loadTest(serverURL string, numClients int, duration time.Duration) { + fmt.Printf("부하 테스트: %d 클라이언트, %v 동안\n", numClients, duration) + var wg sync.WaitGroup + start := time.Now() + + for i := 0; i < numClients; i++ { + wg.Add(1) + go func(id int) { + defer wg.Done() + username := fmt.Sprintf("loadtest_%d_%d", id, rand.Intn(9999)) + c := newClient(id, username, "testpass") + if err := c.connect(serverURL); err != nil { + c.logger.Printf("[FAIL] 연결: %v", err) + return + } + defer c.conn.Close() + + if err := c.stepAuth(); err != nil { + c.logger.Printf("[FAIL] 인증: %v", err) + return + } + + // duration 동안 이동 패킷 전송 + ticker := time.NewTicker(50 * time.Millisecond) + defer ticker.Stop() + timeout := time.After(duration) + x, z := float32(0), float32(0) + + for { + select { + case <-ticker.C: + x += rand.Float32()*2 - 1 + z += rand.Float32()*2 - 1 + _ = c.send(MsgMoveRequest, &pb.MoveRequest{ + Position: &pb.Vector3{X: x, Y: 0, Z: z}, + Velocity: &pb.Vector3{X: 1, Y: 0, Z: 0}, + }) + case <-timeout: + c.logger.Printf("완료 (%.1fs)", time.Since(start).Seconds()) + return + case <-c.done: + return + } + } + }(i) + time.Sleep(10 * time.Millisecond) // 동시 접속 분산 + } + + wg.Wait() + fmt.Printf("부하 테스트 완료: %.1fs\n", time.Since(start).Seconds()) +} + +// ─── main ───────────────────────────────────────────────────────── +func main() { + serverURL := flag.String("url", "ws://localhost:8080/ws", "WebSocket 서버 주소") + username := flag.String("user", "testuser1", "사용자 이름") + password := flag.String("pass", "password123", "비밀번호") + scenario := flag.String("scenario", "all", "실행할 시나리오: all | auth | move | combat | metrics") + numClients := flag.Int("clients", 1, "동시 접속 클라이언트 수 (부하 테스트)") + loadDuration := flag.Duration("duration", 10*time.Second, "부하 테스트 지속 시간") + flag.Parse() + + // 부하 테스트 모드 + if *numClients > 1 { + loadTest(*serverURL, *numClients, *loadDuration) + return + } + + // 단일 클라이언트 시나리오 + c := newClient(1, *username, *password) + if err := c.connect(*serverURL); err != nil { + log.Fatalf("서버 연결 실패: %v\n서버가 실행 중인지 확인하세요: make run", err) + } + defer c.conn.Close() + + c.runScenario(*scenario) + + // Ctrl+C 대기 (실시간 메시지 관찰용) + if *scenario == "all" { + fmt.Println("\n[Ctrl+C로 종료] 서버 메시지 수신 대기 중...") + sig := make(chan os.Signal, 1) + signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) + + ticker := time.NewTicker(50 * time.Millisecond) + defer ticker.Stop() + loop: + for { + select { + case <-sig: + break loop + case <-c.done: + break loop + case msg := <-c.recvCh: + c.handleAsync(msg) + case <-ticker.C: + } + } + } +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..3e766ed --- /dev/null +++ b/config/config.go @@ -0,0 +1,124 @@ +package config + +import ( + "fmt" + "os" + "time" + + "gopkg.in/yaml.v3" +) + +type Config struct { + Server ServerConfig `yaml:"server"` + Database DatabaseConfig `yaml:"database"` + World WorldConfig `yaml:"world"` + Network NetworkConfig `yaml:"network"` + Log LogConfig `yaml:"log"` +} + +type DatabaseConfig struct { + Host string `yaml:"host"` + Port int `yaml:"port"` + User string `yaml:"user"` + Password string `yaml:"password"` + DBName string `yaml:"dbname"` + MaxConns int32 `yaml:"max_conns"` + MinConns int32 `yaml:"min_conns"` + SaveInterval time.Duration `yaml:"save_interval"` +} + +func (d *DatabaseConfig) DSN() string { + return fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=disable", + d.User, d.Password, d.Host, d.Port, d.DBName) +} + +type ServerConfig struct { + Host string `yaml:"host"` + Port int `yaml:"port"` +} + +type WorldConfig struct { + TickRate int `yaml:"tick_rate"` + AOI AOIConfig `yaml:"aoi"` + MaxPlayers int `yaml:"max_players"` +} + +type AOIConfig struct { + Enabled bool `yaml:"enabled"` + CellSize float32 `yaml:"cell_size"` + ViewRange int `yaml:"view_range"` +} + +type NetworkConfig struct { + WriteBufferSize int `yaml:"write_buffer_size"` + ReadBufferSize int `yaml:"read_buffer_size"` + SendChannelSize int `yaml:"send_channel_size"` + HeartbeatInterval time.Duration `yaml:"heartbeat_interval"` + HeartbeatTimeout time.Duration `yaml:"heartbeat_timeout"` + MaxMessageSize int64 `yaml:"max_message_size"` +} + +type LogConfig struct { + Level string `yaml:"level"` +} + +func (c *Config) TickInterval() time.Duration { + return time.Second / time.Duration(c.World.TickRate) +} + +func (c *ServerConfig) Address() string { + return fmt.Sprintf("%s:%d", c.Host, c.Port) +} + +func Load(path string) (*Config, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("read config file: %w", err) + } + + cfg := defaultConfig() + if err := yaml.Unmarshal(data, cfg); err != nil { + return nil, fmt.Errorf("parse config file: %w", err) + } + + return cfg, nil +} + +func defaultConfig() *Config { + return &Config{ + Server: ServerConfig{ + Host: "0.0.0.0", + Port: 8080, + }, + World: WorldConfig{ + TickRate: 20, + MaxPlayers: 5000, + AOI: AOIConfig{ + Enabled: true, + CellSize: 50.0, + ViewRange: 2, + }, + }, + Database: DatabaseConfig{ + Host: "localhost", + Port: 5432, + User: "postgres", + Password: "postgres", + DBName: "mmorpg", + MaxConns: 50, + MinConns: 5, + SaveInterval: 60 * time.Second, + }, + Network: NetworkConfig{ + WriteBufferSize: 4096, + ReadBufferSize: 4096, + SendChannelSize: 256, + HeartbeatInterval: 5 * time.Second, + HeartbeatTimeout: 15 * time.Second, + MaxMessageSize: 8192, + }, + Log: LogConfig{ + Level: "info", + }, + } +} diff --git a/config/config.yaml b/config/config.yaml new file mode 100644 index 0000000..386c417 --- /dev/null +++ b/config/config.yaml @@ -0,0 +1,32 @@ +server: + host: "0.0.0.0" + port: 8080 + +database: + host: "localhost" + port: 5432 + user: "postgres" + password: "postgres" + dbname: "mmorpg" + max_conns: 50 + min_conns: 5 + save_interval: 60s + +world: + tick_rate: 20 + max_players: 5000 + aoi: + enabled: true + cell_size: 50.0 + view_range: 2 + +network: + write_buffer_size: 4096 + read_buffer_size: 4096 + send_channel_size: 256 + heartbeat_interval: 5s + heartbeat_timeout: 15s + max_message_size: 8192 + +log: + level: "info" diff --git a/gen_proto.ps1 b/gen_proto.ps1 new file mode 100644 index 0000000..2d362a6 --- /dev/null +++ b/gen_proto.ps1 @@ -0,0 +1,42 @@ +# proto 코드 생성 스크립트 (PowerShell) +# 사용법: .\gen_proto.ps1 + +$PROTOC = "C:\Users\SSAFY\AppData\Local\Microsoft\WinGet\Packages\Google.Protobuf_Microsoft.Winget.Source_8wekyb3d8bbwe\bin\protoc.exe" +$GOPATH_BIN = (go env GOPATH) + "\bin" +$env:PATH = "$GOPATH_BIN;$env:PATH" + +$ROOT = $PSScriptRoot + +# Go 서버 코드 생성 +Write-Host "[1/2] Generating Go server code..." -ForegroundColor Cyan +& $PROTOC ` + --go_out="$ROOT\proto\gen\pb" ` + --go_opt=paths=source_relative ` + --proto_path="$ROOT\proto" ` + "$ROOT\proto\messages.proto" + +if ($LASTEXITCODE -ne 0) { + Write-Host "Go proto generation failed." -ForegroundColor Red + exit 1 +} +Write-Host " -> proto/gen/pb/messages.pb.go" -ForegroundColor Green + +# Unity C# 코드 생성 +Write-Host "[2/2] Generating C# Unity code..." -ForegroundColor Cyan +$unityOutDir = "$ROOT\unity\Assets\Scripts\Proto" +if (!(Test-Path $unityOutDir)) { + New-Item -ItemType Directory -Path $unityOutDir | Out-Null +} + +& $PROTOC ` + --csharp_out="$unityOutDir" ` + --proto_path="$ROOT\proto" ` + "$ROOT\proto\messages.proto" + +if ($LASTEXITCODE -ne 0) { + Write-Host "C# proto generation failed." -ForegroundColor Red + exit 1 +} +Write-Host " -> unity/Assets/Scripts/Proto/Messages.cs" -ForegroundColor Green + +Write-Host "`nDone!" -ForegroundColor Green diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..26f2093 --- /dev/null +++ b/go.mod @@ -0,0 +1,18 @@ +module a301_game_server + +go 1.25 + +require ( + github.com/gorilla/websocket v1.5.3 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.8.0 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.27.1 // indirect + golang.org/x/crypto v0.48.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/text v0.34.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0bc5b72 --- /dev/null +++ b/go.sum @@ -0,0 +1,31 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= +github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/ai/behavior.go b/internal/ai/behavior.go new file mode 100644 index 0000000..900bc5e --- /dev/null +++ b/internal/ai/behavior.go @@ -0,0 +1,184 @@ +package ai + +import ( + "math" + "time" + + "a301_game_server/internal/entity" + "a301_game_server/pkg/mathutil" +) + +// AIState represents the mob's behavioral state. +type AIState int + +const ( + StateIdle AIState = iota // Standing at spawn, doing nothing + StatePatrol // Wandering near spawn + StateChase // Moving toward a target + StateAttack // In attack range, using skills + StateReturn // Walking back to spawn (leash) + StateDead // Dead, waiting for respawn +) + +// EntityProvider gives the AI access to the game world. +type EntityProvider interface { + GetEntity(id uint64) entity.Entity + GetPlayersInRange(center mathutil.Vec3, radius float32) []entity.Entity +} + +// SkillUser allows the AI to use combat skills. +type SkillUser interface { + UseSkill(casterID uint64, skillID uint32, targetID uint64, targetPos mathutil.Vec3) (bool, string) +} + +// UpdateMob advances one mob's AI by one tick. +func UpdateMob(m *Mob, dt time.Duration, provider EntityProvider, skills SkillUser) { + if !m.IsAlive() { + m.SetState(StateDead) + return + } + + switch m.State() { + case StateIdle: + updateIdle(m, provider) + case StateChase: + updateChase(m, dt, provider) + case StateAttack: + updateAttack(m, provider, skills) + case StateReturn: + updateReturn(m, dt) + case StateDead: + // handled by spawner + } +} + +func updateIdle(m *Mob, provider EntityProvider) { + // Scan for players in aggro range. + target := findNearestPlayer(m, provider, m.Def().AggroRange) + if target != nil { + m.SetTargetID(target.EntityID()) + m.SetState(StateChase) + } +} + +func updateChase(m *Mob, dt time.Duration, provider EntityProvider) { + target := provider.GetEntity(m.TargetID()) + if target == nil || !isAlive(target) { + m.SetTargetID(0) + m.SetState(StateReturn) + return + } + + // Leash check: too far from spawn? + if m.Position().DistanceXZ(m.SpawnPos()) > m.Def().LeashRange { + m.SetTargetID(0) + m.SetState(StateReturn) + return + } + + dist := m.Position().DistanceXZ(target.Position()) + + // Close enough to attack? + if dist <= m.Def().AttackRange { + m.SetState(StateAttack) + return + } + + // Move toward target. + moveToward(m, target.Position(), dt) +} + +func updateAttack(m *Mob, provider EntityProvider, skills SkillUser) { + target := provider.GetEntity(m.TargetID()) + if target == nil || !isAlive(target) { + m.SetTargetID(0) + m.SetState(StateReturn) + return + } + + dist := m.Position().DistanceXZ(target.Position()) + + // Target moved out of attack range? Chase again. + if dist > m.Def().AttackRange*1.2 { // 20% buffer to prevent flickering + m.SetState(StateChase) + return + } + + // Leash check. + if m.Position().DistanceXZ(m.SpawnPos()) > m.Def().LeashRange { + m.SetTargetID(0) + m.SetState(StateReturn) + return + } + + // Face target. + dir := target.Position().Sub(m.Position()) + m.SetRotation(float32(math.Atan2(float64(dir.X), float64(dir.Z)))) + + // Use attack skill. + skills.UseSkill(m.EntityID(), m.Def().AttackSkill, target.EntityID(), mathutil.Vec3{}) +} + +func updateReturn(m *Mob, dt time.Duration) { + dist := m.Position().DistanceXZ(m.SpawnPos()) + if dist < 0.5 { + m.SetPosition(m.SpawnPos()) + m.SetState(StateIdle) + // Heal to full when returning. + m.SetHP(m.MaxHP()) + return + } + moveToward(m, m.SpawnPos(), dt) +} + +// moveToward moves the mob toward a target position at its move speed. +func moveToward(m *Mob, target mathutil.Vec3, dt time.Duration) { + dir := target.Sub(m.Position()) + dir.Y = 0 + dist := dir.Length() + if dist < 0.01 { + return + } + + step := m.Def().MoveSpeed * float32(dt.Seconds()) + if step > dist { + step = dist + } + + move := dir.Normalize().Scale(step) + m.SetPosition(m.Position().Add(move)) + + // Face movement direction. + m.SetRotation(float32(math.Atan2(float64(dir.X), float64(dir.Z)))) +} + +func findNearestPlayer(m *Mob, provider EntityProvider, radius float32) entity.Entity { + players := provider.GetPlayersInRange(m.Position(), radius) + if len(players) == 0 { + return nil + } + + var nearest entity.Entity + minDist := float32(math.MaxFloat32) + for _, p := range players { + if !isAlive(p) { + continue + } + d := m.Position().DistanceXZ(p.Position()) + if d < minDist { + minDist = d + nearest = p + } + } + return nearest +} + +func isAlive(e entity.Entity) bool { + type aliveChecker interface { + IsAlive() bool + } + if a, ok := e.(aliveChecker); ok { + return a.IsAlive() + } + return true +} diff --git a/internal/ai/behavior_test.go b/internal/ai/behavior_test.go new file mode 100644 index 0000000..2c1a287 --- /dev/null +++ b/internal/ai/behavior_test.go @@ -0,0 +1,221 @@ +package ai + +import ( + "testing" + "time" + + "a301_game_server/internal/combat" + "a301_game_server/internal/entity" + "a301_game_server/pkg/mathutil" + pb "a301_game_server/proto/gen/pb" +) + +type mockPlayer struct { + id uint64 + pos mathutil.Vec3 + hp int32 + maxHP int32 + alive bool +} + +func (m *mockPlayer) EntityID() uint64 { return m.id } +func (m *mockPlayer) EntityType() entity.Type { return entity.TypePlayer } +func (m *mockPlayer) Position() mathutil.Vec3 { return m.pos } +func (m *mockPlayer) SetPosition(p mathutil.Vec3) { m.pos = p } +func (m *mockPlayer) Rotation() float32 { return 0 } +func (m *mockPlayer) SetRotation(float32) {} +func (m *mockPlayer) ToProto() *pb.EntityState { return &pb.EntityState{EntityId: m.id} } +func (m *mockPlayer) HP() int32 { return m.hp } +func (m *mockPlayer) MaxHP() int32 { return m.maxHP } +func (m *mockPlayer) SetHP(hp int32) { m.hp = hp; m.alive = hp > 0 } +func (m *mockPlayer) MP() int32 { return 100 } +func (m *mockPlayer) SetMP(int32) {} +func (m *mockPlayer) IsAlive() bool { return m.alive } +func (m *mockPlayer) Stats() combat.CombatStats { return combat.CombatStats{Str: 10, Dex: 10, Int: 10, Level: 1} } + +type mockProvider struct { + entities map[uint64]entity.Entity +} + +func (p *mockProvider) GetEntity(id uint64) entity.Entity { + return p.entities[id] +} + +func (p *mockProvider) GetPlayersInRange(center mathutil.Vec3, radius float32) []entity.Entity { + radiusSq := radius * radius + var result []entity.Entity + for _, e := range p.entities { + if e.EntityType() == entity.TypePlayer { + if e.Position().DistanceSqTo(center) <= radiusSq { + result = append(result, e) + } + } + } + return result +} + +type mockSkillUser struct { + called bool +} + +func (s *mockSkillUser) UseSkill(casterID uint64, skillID uint32, targetID uint64, targetPos mathutil.Vec3) (bool, string) { + s.called = true + return true, "" +} + +func newTestMob() *Mob { + def := &MobDef{ + ID: 1, + Name: "TestMob", + Level: 1, + HP: 100, + MP: 0, + Str: 10, + Dex: 5, + Int: 3, + MoveSpeed: 5.0, + AggroRange: 10.0, + AttackRange: 2.5, + AttackSkill: 1, + LeashRange: 30.0, + } + return NewMob(1000, def, mathutil.NewVec3(50, 0, 50)) +} + +func TestIdleToChase(t *testing.T) { + mob := newTestMob() + player := &mockPlayer{id: 1, pos: mathutil.NewVec3(55, 0, 50), hp: 100, maxHP: 100, alive: true} + + provider := &mockProvider{ + entities: map[uint64]entity.Entity{1: player, 1000: mob}, + } + skills := &mockSkillUser{} + + // Player is within aggro range (5 units < 10 aggro range). + UpdateMob(mob, 50*time.Millisecond, provider, skills) + + if mob.State() != StateChase { + t.Errorf("expected StateChase, got %d", mob.State()) + } + if mob.TargetID() != 1 { + t.Errorf("expected target 1, got %d", mob.TargetID()) + } +} + +func TestChaseToAttack(t *testing.T) { + mob := newTestMob() + mob.SetState(StateChase) + mob.SetTargetID(1) + + // Place player within attack range. + player := &mockPlayer{id: 1, pos: mathutil.NewVec3(52, 0, 50), hp: 100, maxHP: 100, alive: true} + + provider := &mockProvider{ + entities: map[uint64]entity.Entity{1: player, 1000: mob}, + } + skills := &mockSkillUser{} + + UpdateMob(mob, 50*time.Millisecond, provider, skills) + + if mob.State() != StateAttack { + t.Errorf("expected StateAttack, got %d", mob.State()) + } +} + +func TestAttackUsesSkill(t *testing.T) { + mob := newTestMob() + mob.SetState(StateAttack) + mob.SetTargetID(1) + + player := &mockPlayer{id: 1, pos: mathutil.NewVec3(52, 0, 50), hp: 100, maxHP: 100, alive: true} + + provider := &mockProvider{ + entities: map[uint64]entity.Entity{1: player, 1000: mob}, + } + skills := &mockSkillUser{} + + UpdateMob(mob, 50*time.Millisecond, provider, skills) + + if !skills.called { + t.Error("expected skill to be used") + } +} + +func TestLeashReturn(t *testing.T) { + mob := newTestMob() + mob.SetState(StateChase) + mob.SetTargetID(1) + + // Move mob far from spawn. + mob.SetPosition(mathutil.NewVec3(100, 0, 100)) // >30 units from spawn(50,0,50) + + player := &mockPlayer{id: 1, pos: mathutil.NewVec3(110, 0, 110), hp: 100, maxHP: 100, alive: true} + + provider := &mockProvider{ + entities: map[uint64]entity.Entity{1: player, 1000: mob}, + } + skills := &mockSkillUser{} + + UpdateMob(mob, 50*time.Millisecond, provider, skills) + + if mob.State() != StateReturn { + t.Errorf("expected StateReturn (leash), got %d", mob.State()) + } +} + +func TestReturnToIdle(t *testing.T) { + mob := newTestMob() + mob.SetState(StateReturn) + mob.SetPosition(mathutil.NewVec3(50.1, 0, 50.1)) // very close to spawn + + skills := &mockSkillUser{} + provider := &mockProvider{entities: map[uint64]entity.Entity{1000: mob}} + + UpdateMob(mob, 50*time.Millisecond, provider, skills) + + if mob.State() != StateIdle { + t.Errorf("expected StateIdle after return, got %d", mob.State()) + } + if mob.HP() != mob.MaxHP() { + t.Error("mob should heal to full on return") + } +} + +func TestTargetDiesReturnToSpawn(t *testing.T) { + mob := newTestMob() + mob.SetState(StateChase) + mob.SetTargetID(1) + + // Target is dead. + player := &mockPlayer{id: 1, pos: mathutil.NewVec3(55, 0, 50), hp: 0, maxHP: 100, alive: false} + + provider := &mockProvider{ + entities: map[uint64]entity.Entity{1: player, 1000: mob}, + } + skills := &mockSkillUser{} + + UpdateMob(mob, 50*time.Millisecond, provider, skills) + + if mob.State() != StateReturn { + t.Errorf("expected StateReturn when target dies, got %d", mob.State()) + } +} + +func TestMobReset(t *testing.T) { + mob := newTestMob() + mob.SetHP(0) + mob.SetPosition(mathutil.NewVec3(100, 0, 100)) + mob.SetState(StateDead) + + mob.Reset() + + if mob.HP() != mob.MaxHP() { + t.Error("HP should be full after reset") + } + if mob.Position() != mob.SpawnPos() { + t.Error("position should be spawn pos after reset") + } + if mob.State() != StateIdle { + t.Error("state should be Idle after reset") + } +} diff --git a/internal/ai/mob.go b/internal/ai/mob.go new file mode 100644 index 0000000..efc1c83 --- /dev/null +++ b/internal/ai/mob.go @@ -0,0 +1,137 @@ +package ai + +import ( + "a301_game_server/internal/combat" + "a301_game_server/internal/entity" + "a301_game_server/pkg/mathutil" + pb "a301_game_server/proto/gen/pb" +) + +// MobDef defines a mob template loaded from data. +type MobDef struct { + ID uint32 + Name string + Level int32 + HP int32 + MP int32 + Str int32 + Dex int32 + Int int32 + MoveSpeed float32 // units per second + AggroRange float32 // distance to start chasing + AttackRange float32 + AttackSkill uint32 // skill ID used for auto-attack + LeashRange float32 // max distance from spawn before returning + ExpReward int64 + LootTable []LootEntry +} + +// LootEntry defines a possible drop. +type LootEntry struct { + ItemID uint32 + Quantity int32 + Chance float32 // 0.0 - 1.0 +} + +// Mob is a server-controlled enemy entity. +type Mob struct { + id uint64 + def *MobDef + position mathutil.Vec3 + rotation float32 + hp int32 + maxHP int32 + mp int32 + maxMP int32 + spawnPos mathutil.Vec3 + alive bool + + // AI state + state AIState + targetID uint64 // entity being chased/attacked +} + +// NewMob creates a mob from a definition at the given position. +func NewMob(id uint64, def *MobDef, spawnPos mathutil.Vec3) *Mob { + return &Mob{ + id: id, + def: def, + position: spawnPos, + spawnPos: spawnPos, + hp: def.HP, + maxHP: def.HP, + mp: def.MP, + maxMP: def.MP, + alive: true, + state: StateIdle, + } +} + +// Entity interface +func (m *Mob) EntityID() uint64 { return m.id } +func (m *Mob) EntityType() entity.Type { return entity.TypeMob } + +func (m *Mob) Position() mathutil.Vec3 { return m.position } +func (m *Mob) SetPosition(p mathutil.Vec3) { m.position = p } +func (m *Mob) Rotation() float32 { return m.rotation } +func (m *Mob) SetRotation(r float32) { m.rotation = r } + +// Combatant interface +func (m *Mob) HP() int32 { return m.hp } +func (m *Mob) MaxHP() int32 { return m.maxHP } +func (m *Mob) SetHP(hp int32) { + if hp < 0 { + hp = 0 + } + if hp > m.maxHP { + hp = m.maxHP + } + m.hp = hp + m.alive = hp > 0 +} +func (m *Mob) MP() int32 { return m.mp } +func (m *Mob) SetMP(mp int32) { + if mp < 0 { + mp = 0 + } + m.mp = mp +} +func (m *Mob) IsAlive() bool { return m.alive } +func (m *Mob) Stats() combat.CombatStats { + return combat.CombatStats{ + Str: m.def.Str, + Dex: m.def.Dex, + Int: m.def.Int, + Level: m.def.Level, + } +} + +func (m *Mob) Def() *MobDef { return m.def } +func (m *Mob) SpawnPos() mathutil.Vec3 { return m.spawnPos } +func (m *Mob) State() AIState { return m.state } +func (m *Mob) SetState(s AIState) { m.state = s } +func (m *Mob) TargetID() uint64 { return m.targetID } +func (m *Mob) SetTargetID(id uint64) { m.targetID = id } + +func (m *Mob) ToProto() *pb.EntityState { + return &pb.EntityState{ + EntityId: m.id, + Name: m.def.Name, + Position: &pb.Vector3{X: m.position.X, Y: m.position.Y, Z: m.position.Z}, + Rotation: m.rotation, + Hp: m.hp, + MaxHp: m.maxHP, + Level: m.def.Level, + EntityType: pb.EntityType_ENTITY_TYPE_MOB, + } +} + +// Reset restores the mob to full health at its spawn position. +func (m *Mob) Reset() { + m.hp = m.maxHP + m.mp = m.maxMP + m.position = m.spawnPos + m.alive = true + m.state = StateIdle + m.targetID = 0 +} diff --git a/internal/ai/registry.go b/internal/ai/registry.go new file mode 100644 index 0000000..a0a020f --- /dev/null +++ b/internal/ai/registry.go @@ -0,0 +1,130 @@ +package ai + +import "time" + +// MobRegistry holds all mob definitions. +type MobRegistry struct { + defs map[uint32]*MobDef +} + +// NewMobRegistry creates a registry with default mob definitions. +func NewMobRegistry() *MobRegistry { + r := &MobRegistry{defs: make(map[uint32]*MobDef)} + r.registerDefaults() + return r +} + +// Get returns a mob definition by ID. +func (r *MobRegistry) Get(id uint32) *MobDef { + return r.defs[id] +} + +func (r *MobRegistry) registerDefaults() { + r.defs[1] = &MobDef{ + ID: 1, + Name: "Goblin", + Level: 1, + HP: 60, + MP: 0, + Str: 8, + Dex: 6, + Int: 3, + MoveSpeed: 4.0, + AggroRange: 10.0, + AttackRange: 2.5, + AttackSkill: 1, // basic attack + LeashRange: 30.0, + ExpReward: 20, + LootTable: []LootEntry{ + {ItemID: 101, Quantity: 1, Chance: 0.5}, + }, + } + + r.defs[2] = &MobDef{ + ID: 2, + Name: "Wolf", + Level: 2, + HP: 80, + MP: 0, + Str: 12, + Dex: 10, + Int: 2, + MoveSpeed: 6.0, + AggroRange: 12.0, + AttackRange: 2.0, + AttackSkill: 1, + LeashRange: 35.0, + ExpReward: 35, + LootTable: []LootEntry{ + {ItemID: 102, Quantity: 1, Chance: 0.4}, + {ItemID: 103, Quantity: 1, Chance: 0.1}, + }, + } + + r.defs[3] = &MobDef{ + ID: 3, + Name: "Forest Troll", + Level: 5, + HP: 200, + MP: 30, + Str: 25, + Dex: 8, + Int: 5, + MoveSpeed: 3.0, + AggroRange: 8.0, + AttackRange: 3.0, + AttackSkill: 1, + LeashRange: 25.0, + ExpReward: 80, + LootTable: []LootEntry{ + {ItemID: 104, Quantity: 1, Chance: 0.6}, + {ItemID: 105, Quantity: 1, Chance: 0.15}, + }, + } + + r.defs[4] = &MobDef{ + ID: 4, + Name: "Fire Elemental", + Level: 8, + HP: 350, + MP: 100, + Str: 15, + Dex: 12, + Int: 30, + MoveSpeed: 3.5, + AggroRange: 15.0, + AttackRange: 10.0, + AttackSkill: 2, // fireball + LeashRange: 40.0, + ExpReward: 150, + LootTable: []LootEntry{ + {ItemID: 106, Quantity: 1, Chance: 0.3}, + {ItemID: 107, Quantity: 1, Chance: 0.05}, + }, + } + + r.defs[5] = &MobDef{ + ID: 5, + Name: "Dragon Whelp", + Level: 12, + HP: 800, + MP: 200, + Str: 35, + Dex: 20, + Int: 25, + MoveSpeed: 5.0, + AggroRange: 20.0, + AttackRange: 4.0, + AttackSkill: 2, + LeashRange: 50.0, + ExpReward: 350, + LootTable: []LootEntry{ + {ItemID: 108, Quantity: 1, Chance: 0.4}, + {ItemID: 109, Quantity: 1, Chance: 0.02}, + }, + } + + // Adjust respawn-related values (these go in SpawnPoints, not MobDef, but + // set sensible AttackSkill cooldowns via the existing combat skill system) + _ = time.Second // reference for documentation +} diff --git a/internal/ai/spawner.go b/internal/ai/spawner.go new file mode 100644 index 0000000..db2aa32 --- /dev/null +++ b/internal/ai/spawner.go @@ -0,0 +1,152 @@ +package ai + +import ( + "sync/atomic" + "time" + + "a301_game_server/pkg/logger" + "a301_game_server/pkg/mathutil" +) + +// SpawnPoint defines where and what to spawn. +type SpawnPoint struct { + MobDef *MobDef + Position mathutil.Vec3 + RespawnDelay time.Duration + MaxCount int // max alive at this point +} + +// spawnEntry tracks a single spawned mob. +type spawnEntry struct { + mob *Mob + alive bool + diedAt time.Time +} + +// Spawner manages mob spawning and respawning for a zone. +type Spawner struct { + points []SpawnPoint + mobs map[uint64]*spawnEntry // mobID -> entry + pointMobs map[int][]*spawnEntry // spawnPointIndex -> entries + nextID *atomic.Uint64 + + // Callbacks + onSpawn func(m *Mob) + onRemove func(mobID uint64) +} + +// NewSpawner creates a mob spawner. +func NewSpawner(nextID *atomic.Uint64, onSpawn func(*Mob), onRemove func(uint64)) *Spawner { + return &Spawner{ + mobs: make(map[uint64]*spawnEntry), + pointMobs: make(map[int][]*spawnEntry), + nextID: nextID, + onSpawn: onSpawn, + onRemove: onRemove, + } +} + +// AddSpawnPoint registers a spawn point. +func (s *Spawner) AddSpawnPoint(sp SpawnPoint) { + s.points = append(s.points, sp) +} + +// InitialSpawn spawns all mobs at startup. +func (s *Spawner) InitialSpawn() { + for i, sp := range s.points { + for j := 0; j < sp.MaxCount; j++ { + s.spawnMob(i, &sp) + } + } + logger.Info("initial spawn complete", "totalMobs", len(s.mobs)) +} + +// Update checks for mobs that need respawning. +func (s *Spawner) Update(now time.Time) { + for i, sp := range s.points { + entries := s.pointMobs[i] + aliveCount := 0 + for _, e := range entries { + if e.alive { + aliveCount++ + continue + } + // Check if it's time to respawn. + if !e.diedAt.IsZero() && now.Sub(e.diedAt) >= sp.RespawnDelay { + s.respawnMob(e) + aliveCount++ + } + } + // Spawn new if below max count. + for aliveCount < sp.MaxCount { + s.spawnMob(i, &sp) + aliveCount++ + } + } +} + +// NotifyDeath marks a mob as dead and starts the respawn timer. +func (s *Spawner) NotifyDeath(mobID uint64) { + entry, ok := s.mobs[mobID] + if !ok { + return + } + entry.alive = false + entry.diedAt = time.Now() + + // Remove from zone (despawn). + if s.onRemove != nil { + s.onRemove(mobID) + } +} + +// GetMob returns a mob by ID. +func (s *Spawner) GetMob(id uint64) *Mob { + if e, ok := s.mobs[id]; ok { + return e.mob + } + return nil +} + +// AllMobs returns all tracked mobs. +func (s *Spawner) AllMobs() []*Mob { + result := make([]*Mob, 0, len(s.mobs)) + for _, e := range s.mobs { + result = append(result, e.mob) + } + return result +} + +// AliveMobs returns all alive mobs. +func (s *Spawner) AliveMobs() []*Mob { + var result []*Mob + for _, e := range s.mobs { + if e.alive { + result = append(result, e.mob) + } + } + return result +} + +func (s *Spawner) spawnMob(pointIdx int, sp *SpawnPoint) { + id := s.nextID.Add(1) + 100000 // offset to avoid collision with player IDs + mob := NewMob(id, sp.MobDef, sp.Position) + + entry := &spawnEntry{mob: mob, alive: true} + s.mobs[id] = entry + s.pointMobs[pointIdx] = append(s.pointMobs[pointIdx], entry) + + if s.onSpawn != nil { + s.onSpawn(mob) + } +} + +func (s *Spawner) respawnMob(entry *spawnEntry) { + entry.mob.Reset() + entry.alive = true + entry.diedAt = time.Time{} + + if s.onSpawn != nil { + s.onSpawn(entry.mob) + } +} diff --git a/internal/auth/auth.go b/internal/auth/auth.go new file mode 100644 index 0000000..d423857 --- /dev/null +++ b/internal/auth/auth.go @@ -0,0 +1,68 @@ +package auth + +import ( + "context" + "errors" + "fmt" + + "golang.org/x/crypto/bcrypt" + + "a301_game_server/internal/db" +) + +var ( + ErrInvalidCredentials = errors.New("invalid credentials") + ErrUsernameTaken = errors.New("username already taken") +) + +// Service handles account registration and authentication. +type Service struct { + pool *db.Pool +} + +// NewService creates a new auth service. +func NewService(pool *db.Pool) *Service { + return &Service{pool: pool} +} + +// Register creates a new account. Returns the account ID. +func (s *Service) Register(ctx context.Context, username, password string) (int64, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return 0, fmt.Errorf("hash password: %w", err) + } + + var id int64 + err = s.pool.QueryRow(ctx, + `INSERT INTO accounts (username, password) VALUES ($1, $2) + ON CONFLICT (username) DO NOTHING + RETURNING id`, + username, string(hash), + ).Scan(&id) + + if err != nil { + return 0, ErrUsernameTaken + } + + return id, nil +} + +// Login validates credentials and returns the account ID. +func (s *Service) Login(ctx context.Context, username, password string) (int64, error) { + var id int64 + var hash string + + err := s.pool.QueryRow(ctx, + `SELECT id, password FROM accounts WHERE username = $1`, username, + ).Scan(&id, &hash) + + if err != nil { + return 0, ErrInvalidCredentials + } + + if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)); err != nil { + return 0, ErrInvalidCredentials + } + + return id, nil +} diff --git a/internal/combat/buff.go b/internal/combat/buff.go new file mode 100644 index 0000000..25f86b8 --- /dev/null +++ b/internal/combat/buff.go @@ -0,0 +1,86 @@ +package combat + +import "time" + +// BuffDef defines a buff/debuff type. +type BuffDef struct { + ID uint32 + Name string + IsDebuff bool + DamagePerTick int32 // for DoTs (debuff); heal per tick for HoTs (buff) + StatModifier StatMod +} + +// StatMod is a temporary stat modification from a buff. +type StatMod struct { + StrBonus int32 + DexBonus int32 + IntBonus int32 +} + +// ActiveBuff is an active buff/debuff on an entity. +type ActiveBuff struct { + Def *BuffDef + CasterID uint64 + Remaining time.Duration + TickInterval time.Duration + NextTick time.Duration // time until next tick +} + +// Tick advances the buff by dt. Returns damage/heal to apply this tick (0 if no tick). +func (b *ActiveBuff) Tick(dt time.Duration) int32 { + b.Remaining -= dt + var tickValue int32 + + if b.TickInterval > 0 { + b.NextTick -= dt + if b.NextTick <= 0 { + tickValue = b.Def.DamagePerTick + b.NextTick += b.TickInterval + } + } + + return tickValue +} + +// IsExpired returns true if the buff has no remaining duration. +func (b *ActiveBuff) IsExpired() bool { + return b.Remaining <= 0 +} + +// BuffRegistry holds all buff/debuff definitions. +type BuffRegistry struct { + buffs map[uint32]*BuffDef +} + +// NewBuffRegistry creates a registry with default buffs. +func NewBuffRegistry() *BuffRegistry { + r := &BuffRegistry{buffs: make(map[uint32]*BuffDef)} + r.registerDefaults() + return r +} + +// Get returns a buff definition. +func (r *BuffRegistry) Get(id uint32) *BuffDef { + return r.buffs[id] +} + +func (r *BuffRegistry) registerDefaults() { + // Poison DoT (referenced by skill ID 5, effect Value=1) + r.buffs[1] = &BuffDef{ + ID: 1, + Name: "Poison", + IsDebuff: true, + DamagePerTick: 8, + } + + // Power Up buff (referenced by skill ID 6, effect Value=2) + r.buffs[2] = &BuffDef{ + ID: 2, + Name: "Power Up", + IsDebuff: false, + StatModifier: StatMod{ + StrBonus: 20, + }, + } +} diff --git a/internal/combat/combat_manager.go b/internal/combat/combat_manager.go new file mode 100644 index 0000000..34acb80 --- /dev/null +++ b/internal/combat/combat_manager.go @@ -0,0 +1,368 @@ +package combat + +import ( + "time" + + "a301_game_server/internal/entity" + "a301_game_server/internal/network" + "a301_game_server/pkg/mathutil" + pb "a301_game_server/proto/gen/pb" +) + +// Combatant is an entity that can participate in combat. +type Combatant interface { + entity.Entity + HP() int32 + SetHP(int32) + MaxHP() int32 + MP() int32 + SetMP(int32) + IsAlive() bool + Stats() CombatStats +} + +// CombatStats provides stat access for damage calculation. +type CombatStats struct { + Str int32 + Dex int32 + Int int32 + Level int32 +} + +// CombatantWithConn is a combatant that can receive messages (player). +type CombatantWithConn interface { + Combatant + Connection() *network.Connection +} + +// Manager handles all combat logic for a zone. +type Manager struct { + skills *SkillRegistry + buffs *BuffRegistry + + // Per-entity state + cooldowns map[uint64]map[uint32]time.Time // entityID -> skillID -> ready time + activeBuffs map[uint64][]*ActiveBuff // entityID -> active buffs + + // Broadcast function (set by zone to send to AOI) + broadcastToNearby func(ent entity.Entity, msgType uint16, msg interface{}) + sendToEntity func(entityID uint64, msgType uint16, msg interface{}) +} + +// NewManager creates a combat manager. +func NewManager() *Manager { + return &Manager{ + skills: NewSkillRegistry(), + buffs: NewBuffRegistry(), + cooldowns: make(map[uint64]map[uint32]time.Time), + activeBuffs: make(map[uint64][]*ActiveBuff), + } +} + +// Skills returns the skill registry. +func (m *Manager) Skills() *SkillRegistry { return m.skills } + +// SetBroadcast configures the broadcast callback. +func (m *Manager) SetBroadcast( + broadcast func(ent entity.Entity, msgType uint16, msg interface{}), + send func(entityID uint64, msgType uint16, msg interface{}), +) { + m.broadcastToNearby = broadcast + m.sendToEntity = send +} + +// UseSkill attempts to execute a skill. +func (m *Manager) UseSkill( + caster Combatant, + skillID uint32, + targetID uint64, + targetPos mathutil.Vec3, + getEntity func(uint64) entity.Entity, + getEntitiesInRadius func(center mathutil.Vec3, radius float32) []entity.Entity, +) (bool, string) { + + if !caster.IsAlive() { + return false, "you are dead" + } + + skill := m.skills.Get(skillID) + if skill == nil { + return false, "unknown skill" + } + + // Cooldown check. + if cd, ok := m.cooldowns[caster.EntityID()]; ok { + if readyAt, ok := cd[skillID]; ok && time.Now().Before(readyAt) { + return false, "skill on cooldown" + } + } + + // Mana check. + if caster.MP() < skill.ManaCost { + return false, "not enough mana" + } + + // Resolve targets. + var targets []Combatant + + switch skill.TargetType { + case TargetSelf: + targets = []Combatant{caster} + + case TargetSingleEnemy: + ent := getEntity(targetID) + if ent == nil { + return false, "target not found" + } + target, ok := ent.(Combatant) + if !ok || !target.IsAlive() { + return false, "invalid target" + } + if caster.Position().DistanceXZ(target.Position()) > skill.Range { + return false, "target out of range" + } + targets = []Combatant{target} + + case TargetSingleAlly: + ent := getEntity(targetID) + if ent == nil { + return false, "target not found" + } + target, ok := ent.(Combatant) + if !ok || !target.IsAlive() { + return false, "invalid target" + } + if caster.Position().DistanceXZ(target.Position()) > skill.Range { + return false, "target out of range" + } + targets = []Combatant{target} + + case TargetAoEGround: + if caster.Position().DistanceXZ(targetPos) > skill.Range { + return false, "target position out of range" + } + entities := getEntitiesInRadius(targetPos, skill.AoERadius) + for _, e := range entities { + if c, ok := e.(Combatant); ok && c.IsAlive() && c.EntityID() != caster.EntityID() { + targets = append(targets, c) + } + } + + case TargetAoETarget: + ent := getEntity(targetID) + if ent == nil { + return false, "target not found" + } + if caster.Position().DistanceXZ(ent.Position()) > skill.Range { + return false, "target out of range" + } + entities := getEntitiesInRadius(ent.Position(), skill.AoERadius) + for _, e := range entities { + if c, ok := e.(Combatant); ok && c.IsAlive() && c.EntityID() != caster.EntityID() { + targets = append(targets, c) + } + } + } + + // Consume mana. + caster.SetMP(caster.MP() - skill.ManaCost) + + // Set cooldown. + if m.cooldowns[caster.EntityID()] == nil { + m.cooldowns[caster.EntityID()] = make(map[uint32]time.Time) + } + m.cooldowns[caster.EntityID()][skillID] = time.Now().Add(skill.Cooldown) + + // Calculate effective stats (base + buff modifiers). + casterStats := m.effectiveStats(caster) + + // Apply effects to each target. + for _, target := range targets { + m.applyEffects(caster, target, skill, casterStats) + } + + return true, "" +} + +func (m *Manager) applyEffects(caster, target Combatant, skill *SkillDef, casterStats CombatStats) { + for _, effect := range skill.Effects { + switch effect.Type { + case EffectDamage: + targetStats := m.effectiveStats(target) + result := CalcDamage(effect.Value, casterStats.Str, targetStats.Dex) + target.SetHP(target.HP() - result.FinalDamage) + + died := !target.IsAlive() + + evt := &pb.CombatEvent{ + CasterId: caster.EntityID(), + TargetId: target.EntityID(), + SkillId: skill.ID, + Damage: result.FinalDamage, + IsCritical: result.IsCritical, + TargetDied: died, + TargetHp: target.HP(), + TargetMaxHp: target.MaxHP(), + EventType: pb.CombatEventType_COMBAT_EVENT_DAMAGE, + } + + if m.broadcastToNearby != nil { + m.broadcastToNearby(target, network.MsgCombatEvent, evt) + } + + if died { + deathEvt := &pb.CombatEvent{ + CasterId: caster.EntityID(), + TargetId: target.EntityID(), + EventType: pb.CombatEventType_COMBAT_EVENT_DEATH, + } + if m.broadcastToNearby != nil { + m.broadcastToNearby(target, network.MsgCombatEvent, deathEvt) + } + } + + case EffectHeal: + heal := CalcHeal(effect.Value, casterStats.Int) + target.SetHP(target.HP() + heal) + + evt := &pb.CombatEvent{ + CasterId: caster.EntityID(), + TargetId: target.EntityID(), + SkillId: skill.ID, + Heal: heal, + TargetHp: target.HP(), + TargetMaxHp: target.MaxHP(), + EventType: pb.CombatEventType_COMBAT_EVENT_HEAL, + } + if m.broadcastToNearby != nil { + m.broadcastToNearby(target, network.MsgCombatEvent, evt) + } + + case EffectBuff, EffectDebuff: + buffDef := m.buffs.Get(uint32(effect.Value)) + if buffDef == nil { + continue + } + ab := &ActiveBuff{ + Def: buffDef, + CasterID: caster.EntityID(), + Remaining: effect.Duration, + TickInterval: effect.TickInterval, + NextTick: effect.TickInterval, + } + m.activeBuffs[target.EntityID()] = append(m.activeBuffs[target.EntityID()], ab) + + evt := &pb.BuffApplied{ + TargetId: target.EntityID(), + BuffId: buffDef.ID, + BuffName: buffDef.Name, + Duration: float32(effect.Duration.Seconds()), + IsDebuff: buffDef.IsDebuff, + } + if m.broadcastToNearby != nil { + m.broadcastToNearby(target, network.MsgBuffApplied, evt) + } + } + } +} + +// UpdateBuffs processes active buffs each tick. Call once per zone tick. +func (m *Manager) UpdateBuffs(dt time.Duration, getEntity func(uint64) Combatant) { + for entityID, buffs := range m.activeBuffs { + target := getEntity(entityID) + if target == nil { + delete(m.activeBuffs, entityID) + continue + } + + var remaining []*ActiveBuff + for _, b := range buffs { + tickValue := b.Tick(dt) + + if tickValue != 0 && target.IsAlive() { + if b.Def.IsDebuff { + // DoT damage. + target.SetHP(target.HP() - tickValue) + evt := &pb.CombatEvent{ + CasterId: b.CasterID, + TargetId: entityID, + Damage: tickValue, + TargetHp: target.HP(), + TargetMaxHp: target.MaxHP(), + EventType: pb.CombatEventType_COMBAT_EVENT_DAMAGE, + } + if m.broadcastToNearby != nil { + m.broadcastToNearby(target, network.MsgCombatEvent, evt) + } + } else { + // HoT heal. + target.SetHP(target.HP() + tickValue) + evt := &pb.CombatEvent{ + CasterId: b.CasterID, + TargetId: entityID, + Heal: tickValue, + TargetHp: target.HP(), + TargetMaxHp: target.MaxHP(), + EventType: pb.CombatEventType_COMBAT_EVENT_HEAL, + } + if m.broadcastToNearby != nil { + m.broadcastToNearby(target, network.MsgCombatEvent, evt) + } + } + } + + if !b.IsExpired() { + remaining = append(remaining, b) + } else { + // Notify buff removed. + evt := &pb.BuffRemoved{ + TargetId: entityID, + BuffId: b.Def.ID, + } + if m.broadcastToNearby != nil { + m.broadcastToNearby(target, network.MsgBuffRemoved, evt) + } + } + } + + if len(remaining) == 0 { + delete(m.activeBuffs, entityID) + } else { + m.activeBuffs[entityID] = remaining + } + } +} + +// Respawn resets a dead entity to full HP at a spawn position. +func (m *Manager) Respawn(ent Combatant, spawnPos mathutil.Vec3) { + ent.SetHP(ent.MaxHP()) + ent.SetPosition(spawnPos) + m.clearCooldowns(ent.EntityID()) + m.clearBuffs(ent.EntityID()) +} + +// RemoveEntity cleans up combat state for a removed entity. +func (m *Manager) RemoveEntity(entityID uint64) { + m.clearCooldowns(entityID) + m.clearBuffs(entityID) +} + +// effectiveStats returns stats with buff modifiers applied. +func (m *Manager) effectiveStats(c Combatant) CombatStats { + base := c.Stats() + buffs := m.activeBuffs[c.EntityID()] + for _, b := range buffs { + base.Str += b.Def.StatModifier.StrBonus + base.Dex += b.Def.StatModifier.DexBonus + base.Int += b.Def.StatModifier.IntBonus + } + return base +} + +func (m *Manager) clearCooldowns(entityID uint64) { + delete(m.cooldowns, entityID) +} + +func (m *Manager) clearBuffs(entityID uint64) { + delete(m.activeBuffs, entityID) +} diff --git a/internal/combat/combat_test.go b/internal/combat/combat_test.go new file mode 100644 index 0000000..8779447 --- /dev/null +++ b/internal/combat/combat_test.go @@ -0,0 +1,239 @@ +package combat + +import ( + "testing" + "time" + + "a301_game_server/internal/entity" + "a301_game_server/pkg/mathutil" + pb "a301_game_server/proto/gen/pb" +) + +// mockCombatant implements Combatant for testing. +type mockCombatant struct { + id uint64 + pos mathutil.Vec3 + hp, maxHP int32 + mp, maxMP int32 + str, dex, intStat int32 +} + +func (m *mockCombatant) EntityID() uint64 { return m.id } +func (m *mockCombatant) EntityType() entity.Type { return entity.TypePlayer } +func (m *mockCombatant) Position() mathutil.Vec3 { return m.pos } +func (m *mockCombatant) SetPosition(p mathutil.Vec3) { m.pos = p } +func (m *mockCombatant) Rotation() float32 { return 0 } +func (m *mockCombatant) SetRotation(float32) {} +func (m *mockCombatant) ToProto() *pb.EntityState { return &pb.EntityState{EntityId: m.id} } +func (m *mockCombatant) HP() int32 { return m.hp } +func (m *mockCombatant) SetHP(hp int32) { + if hp < 0 { hp = 0 } + if hp > m.maxHP { hp = m.maxHP } + m.hp = hp +} +func (m *mockCombatant) MaxHP() int32 { return m.maxHP } +func (m *mockCombatant) MP() int32 { return m.mp } +func (m *mockCombatant) SetMP(mp int32) { + if mp < 0 { mp = 0 } + m.mp = mp +} +func (m *mockCombatant) IsAlive() bool { return m.hp > 0 } +func (m *mockCombatant) Stats() CombatStats { + return CombatStats{Str: m.str, Dex: m.dex, Int: m.intStat, Level: 1} +} + +func newMock(id uint64, x, z float32) *mockCombatant { + return &mockCombatant{ + id: id, pos: mathutil.NewVec3(x, 0, z), + hp: 100, maxHP: 100, mp: 100, maxMP: 100, + str: 10, dex: 10, intStat: 10, + } +} + +func TestBasicAttack(t *testing.T) { + mgr := NewManager() + mgr.SetBroadcast(func(entity.Entity, uint16, interface{}) {}, func(uint64, uint16, interface{}) {}) + + attacker := newMock(1, 0, 0) + target := newMock(2, 2, 0) // within range (3.0) + + entities := map[uint64]*mockCombatant{1: attacker, 2: target} + + ok, errMsg := mgr.UseSkill(attacker, 1, 2, mathutil.Vec3{}, + func(id uint64) entity.Entity { + if e, ok := entities[id]; ok { return e } + return nil + }, + nil, + ) + + if !ok { + t.Fatalf("expected success, got error: %s", errMsg) + } + + if target.HP() >= 100 { + t.Error("target should have taken damage") + } +} + +func TestOutOfRange(t *testing.T) { + mgr := NewManager() + mgr.SetBroadcast(func(entity.Entity, uint16, interface{}) {}, func(uint64, uint16, interface{}) {}) + + attacker := newMock(1, 0, 0) + target := newMock(2, 100, 0) // far away + + entities := map[uint64]*mockCombatant{1: attacker, 2: target} + + ok, _ := mgr.UseSkill(attacker, 1, 2, mathutil.Vec3{}, + func(id uint64) entity.Entity { + if e, ok := entities[id]; ok { return e } + return nil + }, + nil, + ) + + if ok { + t.Error("expected failure due to range") + } +} + +func TestCooldown(t *testing.T) { + mgr := NewManager() + mgr.SetBroadcast(func(entity.Entity, uint16, interface{}) {}, func(uint64, uint16, interface{}) {}) + + attacker := newMock(1, 0, 0) + target := newMock(2, 2, 0) + + entities := map[uint64]*mockCombatant{1: attacker, 2: target} + getEnt := func(id uint64) entity.Entity { + if e, ok := entities[id]; ok { return e } + return nil + } + + // First use should succeed. + ok, _ := mgr.UseSkill(attacker, 1, 2, mathutil.Vec3{}, getEnt, nil) + if !ok { + t.Fatal("first use should succeed") + } + + // Immediate second use should fail (cooldown). + ok, errMsg := mgr.UseSkill(attacker, 1, 2, mathutil.Vec3{}, getEnt, nil) + if ok { + t.Error("expected cooldown failure") + } + if errMsg != "skill on cooldown" { + t.Errorf("expected 'skill on cooldown', got '%s'", errMsg) + } +} + +func TestManaConsumption(t *testing.T) { + mgr := NewManager() + mgr.SetBroadcast(func(entity.Entity, uint16, interface{}) {}, func(uint64, uint16, interface{}) {}) + + caster := newMock(1, 0, 0) + target := newMock(2, 5, 0) + caster.mp = 10 // not enough for Fireball (20 mana) + + entities := map[uint64]*mockCombatant{1: caster, 2: target} + + ok, errMsg := mgr.UseSkill(caster, 2, 2, mathutil.Vec3{}, + func(id uint64) entity.Entity { + if e, ok := entities[id]; ok { return e } + return nil + }, + nil, + ) + + if ok { + t.Error("expected mana failure") + } + if errMsg != "not enough mana" { + t.Errorf("expected 'not enough mana', got '%s'", errMsg) + } +} + +func TestHealSelf(t *testing.T) { + mgr := NewManager() + mgr.SetBroadcast(func(entity.Entity, uint16, interface{}) {}, func(uint64, uint16, interface{}) {}) + + caster := newMock(1, 0, 0) + caster.hp = 30 + + ok, _ := mgr.UseSkill(caster, 3, 0, mathutil.Vec3{}, + func(id uint64) entity.Entity { return nil }, + nil, + ) + + if !ok { + t.Fatal("heal should succeed") + } + if caster.HP() <= 30 { + t.Error("HP should have increased") + } +} + +func TestPoisonDoT(t *testing.T) { + mgr := NewManager() + mgr.SetBroadcast(func(entity.Entity, uint16, interface{}) {}, func(uint64, uint16, interface{}) {}) + + attacker := newMock(1, 0, 0) + target := newMock(2, 5, 0) + + entities := map[uint64]*mockCombatant{1: attacker, 2: target} + + // Apply poison (skill 5). + ok, _ := mgr.UseSkill(attacker, 5, 2, mathutil.Vec3{}, + func(id uint64) entity.Entity { + if e, ok := entities[id]; ok { return e } + return nil + }, + nil, + ) + if !ok { + t.Fatal("poison should succeed") + } + + initialHP := target.HP() + + // Simulate ticks until first DoT tick (2s at 50ms/tick = 40 ticks). + for i := 0; i < 40; i++ { + mgr.UpdateBuffs(50*time.Millisecond, func(id uint64) Combatant { + if e, ok := entities[id]; ok { return e } + return nil + }) + } + + if target.HP() >= initialHP { + t.Error("poison DoT should have dealt damage") + } +} + +func TestDamageFormula(t *testing.T) { + // Base 15, attacker STR 10 (1.1x), defender DEX 10 (0.95x) + // Expected ~15.675 (without crit). + result := CalcDamage(15, 10, 10) + if result.FinalDamage < 1 { + t.Error("damage should be at least 1") + } +} + +func TestRespawn(t *testing.T) { + mgr := NewManager() + + ent := newMock(1, 50, 50) + ent.hp = 0 + + spawn := mathutil.NewVec3(0, 0, 0) + mgr.Respawn(ent, spawn) + + if !ent.IsAlive() { + t.Error("should be alive after respawn") + } + if ent.HP() != ent.MaxHP() { + t.Error("should have full HP after respawn") + } + if ent.Position() != spawn { + t.Error("should be at spawn position") + } +} diff --git a/internal/combat/damage.go b/internal/combat/damage.go new file mode 100644 index 0000000..71bf548 --- /dev/null +++ b/internal/combat/damage.go @@ -0,0 +1,51 @@ +package combat + +import ( + "math/rand" +) + +const ( + critChance = 0.15 // 15% crit chance + critMultiplier = 1.5 +) + +// DamageResult holds the outcome of a damage calculation. +type DamageResult struct { + FinalDamage int32 + IsCritical bool +} + +// CalcDamage computes final damage from base damage and attacker/defender stats. +// Formula: base * (1 + attackerStr/100) * (1 - defenderDex/200) +// Then roll for crit. +func CalcDamage(baseDamage int32, attackerStr, defenderDex int32) DamageResult { + attack := float64(baseDamage) * (1.0 + float64(attackerStr)/100.0) + defense := 1.0 - float64(defenderDex)/200.0 + if defense < 0.1 { + defense = 0.1 // minimum 10% damage + } + + dmg := attack * defense + isCrit := rand.Float64() < critChance + if isCrit { + dmg *= critMultiplier + } + + final := int32(dmg) + if final < 1 { + final = 1 + } + + return DamageResult{FinalDamage: final, IsCritical: isCrit} +} + +// CalcHeal computes final healing. +// Formula: base * (1 + casterInt/100) +func CalcHeal(baseHeal int32, casterInt int32) int32 { + heal := float64(baseHeal) * (1.0 + float64(casterInt)/100.0) + result := int32(heal) + if result < 1 { + result = 1 + } + return result +} diff --git a/internal/combat/skill.go b/internal/combat/skill.go new file mode 100644 index 0000000..8ab5bdc --- /dev/null +++ b/internal/combat/skill.go @@ -0,0 +1,148 @@ +package combat + +import "time" + +// TargetType determines how a skill selects its target. +type TargetType int + +const ( + TargetSelf TargetType = iota + TargetSingleEnemy // requires a valid enemy entity ID + TargetSingleAlly // requires a valid ally entity ID + TargetAoEGround // requires a ground position + TargetAoETarget // AoE centered on target entity +) + +// EffectType determines what a skill effect does. +type EffectType int + +const ( + EffectDamage EffectType = iota + EffectHeal + EffectBuff + EffectDebuff +) + +// Effect is a single outcome of a skill. +type Effect struct { + Type EffectType + Value int32 // damage amount, heal amount, or buff/debuff ID + Duration time.Duration // 0 for instant effects + TickInterval time.Duration // for DoT/HoT; 0 means apply once +} + +// SkillDef defines a skill's properties (loaded from data). +type SkillDef struct { + ID uint32 + Name string + Cooldown time.Duration + ManaCost int32 + Range float32 // max distance to target (0 = self only) + TargetType TargetType + AoERadius float32 // for AoE skills + CastTime time.Duration + Effects []Effect +} + +// SkillRegistry holds all skill definitions. +type SkillRegistry struct { + skills map[uint32]*SkillDef +} + +// NewSkillRegistry creates a registry with default skills. +func NewSkillRegistry() *SkillRegistry { + r := &SkillRegistry{skills: make(map[uint32]*SkillDef)} + r.registerDefaults() + return r +} + +// Get returns a skill definition by ID. +func (r *SkillRegistry) Get(id uint32) *SkillDef { + return r.skills[id] +} + +// Register adds a skill definition. +func (r *SkillRegistry) Register(s *SkillDef) { + r.skills[s.ID] = s +} + +func (r *SkillRegistry) registerDefaults() { + // Basic attack + r.Register(&SkillDef{ + ID: 1, + Name: "Basic Attack", + Cooldown: 1 * time.Second, + ManaCost: 0, + Range: 3.0, + TargetType: TargetSingleEnemy, + Effects: []Effect{ + {Type: EffectDamage, Value: 15}, + }, + }) + + // Fireball - ranged damage + r.Register(&SkillDef{ + ID: 2, + Name: "Fireball", + Cooldown: 3 * time.Second, + ManaCost: 20, + Range: 15.0, + TargetType: TargetSingleEnemy, + Effects: []Effect{ + {Type: EffectDamage, Value: 40}, + }, + }) + + // Heal + r.Register(&SkillDef{ + ID: 3, + Name: "Heal", + Cooldown: 5 * time.Second, + ManaCost: 30, + Range: 0, + TargetType: TargetSelf, + Effects: []Effect{ + {Type: EffectHeal, Value: 50}, + }, + }) + + // AoE - Flame Strike + r.Register(&SkillDef{ + ID: 4, + Name: "Flame Strike", + Cooldown: 8 * time.Second, + ManaCost: 40, + Range: 12.0, + TargetType: TargetAoEGround, + AoERadius: 5.0, + Effects: []Effect{ + {Type: EffectDamage, Value: 30}, + }, + }) + + // Poison (DoT debuff) + r.Register(&SkillDef{ + ID: 5, + Name: "Poison", + Cooldown: 6 * time.Second, + ManaCost: 15, + Range: 8.0, + TargetType: TargetSingleEnemy, + Effects: []Effect{ + {Type: EffectDebuff, Value: 1, Duration: 10 * time.Second, TickInterval: 2 * time.Second}, + }, + }) + + // Power Buff (self buff - increases damage) + r.Register(&SkillDef{ + ID: 6, + Name: "Power Up", + Cooldown: 15 * time.Second, + ManaCost: 25, + Range: 0, + TargetType: TargetSelf, + Effects: []Effect{ + {Type: EffectBuff, Value: 2, Duration: 10 * time.Second}, + }, + }) +} diff --git a/internal/db/migrations.go b/internal/db/migrations.go new file mode 100644 index 0000000..818648e --- /dev/null +++ b/internal/db/migrations.go @@ -0,0 +1,47 @@ +package db + +var migrations = []string{ + // 0: accounts table + `CREATE TABLE IF NOT EXISTS accounts ( + id BIGSERIAL PRIMARY KEY, + username VARCHAR(32) UNIQUE NOT NULL, + password VARCHAR(128) NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + )`, + + // 1: characters table + `CREATE TABLE IF NOT EXISTS characters ( + id BIGSERIAL PRIMARY KEY, + account_id BIGINT NOT NULL REFERENCES accounts(id), + name VARCHAR(32) UNIQUE NOT NULL, + level INT NOT NULL DEFAULT 1, + exp BIGINT NOT NULL DEFAULT 0, + hp INT NOT NULL DEFAULT 100, + max_hp INT NOT NULL DEFAULT 100, + mp INT NOT NULL DEFAULT 50, + max_mp INT NOT NULL DEFAULT 50, + str INT NOT NULL DEFAULT 10, + dex INT NOT NULL DEFAULT 10, + int_stat INT NOT NULL DEFAULT 10, + zone_id INT NOT NULL DEFAULT 1, + pos_x REAL NOT NULL DEFAULT 0, + pos_y REAL NOT NULL DEFAULT 0, + pos_z REAL NOT NULL DEFAULT 0, + rotation REAL NOT NULL DEFAULT 0, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + )`, + + // 2: inventory table + `CREATE TABLE IF NOT EXISTS inventory ( + id BIGSERIAL PRIMARY KEY, + character_id BIGINT NOT NULL REFERENCES characters(id) ON DELETE CASCADE, + slot INT NOT NULL, + item_id INT NOT NULL, + quantity INT NOT NULL DEFAULT 1, + UNIQUE(character_id, slot) + )`, + + // 3: index for character lookups by account + `CREATE INDEX IF NOT EXISTS idx_characters_account_id ON characters(account_id)`, +} diff --git a/internal/db/postgres.go b/internal/db/postgres.go new file mode 100644 index 0000000..d8b3ef3 --- /dev/null +++ b/internal/db/postgres.go @@ -0,0 +1,51 @@ +package db + +import ( + "context" + "fmt" + + "github.com/jackc/pgx/v5/pgxpool" + + "a301_game_server/config" + "a301_game_server/pkg/logger" +) + +// Pool wraps a pgx connection pool. +type Pool struct { + *pgxpool.Pool +} + +// NewPool creates a connection pool to PostgreSQL. +func NewPool(ctx context.Context, cfg *config.DatabaseConfig) (*Pool, error) { + poolCfg, err := pgxpool.ParseConfig(cfg.DSN()) + if err != nil { + return nil, fmt.Errorf("parse dsn: %w", err) + } + + poolCfg.MaxConns = cfg.MaxConns + poolCfg.MinConns = cfg.MinConns + + pool, err := pgxpool.NewWithConfig(ctx, poolCfg) + if err != nil { + return nil, fmt.Errorf("create pool: %w", err) + } + + if err := pool.Ping(ctx); err != nil { + pool.Close() + return nil, fmt.Errorf("ping database: %w", err) + } + + logger.Info("database connected", "host", cfg.Host, "db", cfg.DBName) + return &Pool{pool}, nil +} + +// RunMigrations executes all schema migrations. +func (p *Pool) RunMigrations(ctx context.Context) error { + for i, m := range migrations { + if _, err := p.Exec(ctx, m); err != nil { + return fmt.Errorf("migration %d failed: %w", i, err) + } + } + logger.Info("database migrations completed", "count", len(migrations)) + return nil +} diff --git a/internal/db/repository/character.go b/internal/db/repository/character.go new file mode 100644 index 0000000..33007d2 --- /dev/null +++ b/internal/db/repository/character.go @@ -0,0 +1,166 @@ +package repository + +import ( + "context" + "fmt" + + "a301_game_server/internal/db" +) + +// CharacterData holds persisted character state. +type CharacterData struct { + ID int64 + AccountID int64 + Name string + Level int32 + Exp int64 + HP int32 + MaxHP int32 + MP int32 + MaxMP int32 + Str int32 + Dex int32 + IntStat int32 + ZoneID int32 + PosX float32 + PosY float32 + PosZ float32 + Rotation float32 +} + +// CharacterRepo handles character persistence. +type CharacterRepo struct { + pool *db.Pool +} + +// NewCharacterRepo creates a new character repository. +func NewCharacterRepo(pool *db.Pool) *CharacterRepo { + return &CharacterRepo{pool: pool} +} + +// Create inserts a new character. +func (r *CharacterRepo) Create(ctx context.Context, accountID int64, name string) (*CharacterData, error) { + c := &CharacterData{ + AccountID: accountID, + Name: name, + Level: 1, + HP: 100, + MaxHP: 100, + MP: 50, + MaxMP: 50, + Str: 10, + Dex: 10, + IntStat: 10, + ZoneID: 1, + } + + err := r.pool.QueryRow(ctx, + `INSERT INTO characters (account_id, name, level, hp, max_hp, mp, max_mp, str, dex, int_stat, zone_id) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) + RETURNING id`, + c.AccountID, c.Name, c.Level, c.HP, c.MaxHP, c.MP, c.MaxMP, c.Str, c.Dex, c.IntStat, c.ZoneID, + ).Scan(&c.ID) + + if err != nil { + return nil, fmt.Errorf("create character: %w", err) + } + return c, nil +} + +// GetByAccountID returns all characters for an account. +func (r *CharacterRepo) GetByAccountID(ctx context.Context, accountID int64) ([]*CharacterData, error) { + rows, err := r.pool.Query(ctx, + `SELECT id, account_id, name, level, exp, hp, max_hp, mp, max_mp, str, dex, int_stat, + zone_id, pos_x, pos_y, pos_z, rotation + FROM characters WHERE account_id = $1`, accountID, + ) + if err != nil { + return nil, fmt.Errorf("query characters: %w", err) + } + defer rows.Close() + + var chars []*CharacterData + for rows.Next() { + c := &CharacterData{} + if err := rows.Scan( + &c.ID, &c.AccountID, &c.Name, &c.Level, &c.Exp, + &c.HP, &c.MaxHP, &c.MP, &c.MaxMP, + &c.Str, &c.Dex, &c.IntStat, + &c.ZoneID, &c.PosX, &c.PosY, &c.PosZ, &c.Rotation, + ); err != nil { + return nil, fmt.Errorf("scan character: %w", err) + } + chars = append(chars, c) + } + return chars, nil +} + +// GetByID loads a single character. +func (r *CharacterRepo) GetByID(ctx context.Context, id int64) (*CharacterData, error) { + c := &CharacterData{} + err := r.pool.QueryRow(ctx, + `SELECT id, account_id, name, level, exp, hp, max_hp, mp, max_mp, str, dex, int_stat, + zone_id, pos_x, pos_y, pos_z, rotation + FROM characters WHERE id = $1`, id, + ).Scan( + &c.ID, &c.AccountID, &c.Name, &c.Level, &c.Exp, + &c.HP, &c.MaxHP, &c.MP, &c.MaxMP, + &c.Str, &c.Dex, &c.IntStat, + &c.ZoneID, &c.PosX, &c.PosY, &c.PosZ, &c.Rotation, + ) + if err != nil { + return nil, fmt.Errorf("get character %d: %w", id, err) + } + return c, nil +} + +// Save persists the current character state. +func (r *CharacterRepo) Save(ctx context.Context, c *CharacterData) error { + _, err := r.pool.Exec(ctx, + `UPDATE characters SET + level = $2, exp = $3, hp = $4, max_hp = $5, mp = $6, max_mp = $7, + str = $8, dex = $9, int_stat = $10, + zone_id = $11, pos_x = $12, pos_y = $13, pos_z = $14, rotation = $15, + updated_at = NOW() + WHERE id = $1`, + c.ID, c.Level, c.Exp, c.HP, c.MaxHP, c.MP, c.MaxMP, + c.Str, c.Dex, c.IntStat, + c.ZoneID, c.PosX, c.PosY, c.PosZ, c.Rotation, + ) + if err != nil { + return fmt.Errorf("save character %d: %w", c.ID, err) + } + return nil +} + +// SaveBatch saves multiple characters in a single transaction. +func (r *CharacterRepo) SaveBatch(ctx context.Context, chars []*CharacterData) error { + if len(chars) == 0 { + return nil + } + + tx, err := r.pool.Begin(ctx) + if err != nil { + return fmt.Errorf("begin tx: %w", err) + } + defer tx.Rollback(ctx) + + for _, c := range chars { + _, err := tx.Exec(ctx, + `UPDATE characters SET + level = $2, exp = $3, hp = $4, max_hp = $5, mp = $6, max_mp = $7, + str = $8, dex = $9, int_stat = $10, + zone_id = $11, pos_x = $12, pos_y = $13, pos_z = $14, rotation = $15, + updated_at = NOW() + WHERE id = $1`, + c.ID, c.Level, c.Exp, c.HP, c.MaxHP, c.MP, c.MaxMP, + c.Str, c.Dex, c.IntStat, + c.ZoneID, c.PosX, c.PosY, c.PosZ, c.Rotation, + ) + if err != nil { + return fmt.Errorf("save character %d in batch: %w", c.ID, err) + } + } + + return tx.Commit(ctx) +} diff --git a/internal/entity/entity.go b/internal/entity/entity.go new file mode 100644 index 0000000..dd07fa9 --- /dev/null +++ b/internal/entity/entity.go @@ -0,0 +1,26 @@ +package entity + +import ( + pb "a301_game_server/proto/gen/pb" + "a301_game_server/pkg/mathutil" +) + +// Type identifies the kind of entity. +type Type int + +const ( + TypePlayer Type = iota + TypeMob + TypeNPC +) + +// Entity is anything that exists in the game world. +type Entity interface { + EntityID() uint64 + EntityType() Type + Position() mathutil.Vec3 + SetPosition(pos mathutil.Vec3) + Rotation() float32 + SetRotation(rot float32) + ToProto() *pb.EntityState +} diff --git a/internal/game/game_server.go b/internal/game/game_server.go new file mode 100644 index 0000000..88c08c7 --- /dev/null +++ b/internal/game/game_server.go @@ -0,0 +1,462 @@ +package game + +import ( + "context" + "sync" + "sync/atomic" + "time" + + "a301_game_server/config" + "a301_game_server/internal/ai" + "a301_game_server/internal/auth" + "a301_game_server/internal/db" + "a301_game_server/internal/db/repository" + "a301_game_server/internal/network" + "a301_game_server/internal/player" + "a301_game_server/internal/world" + "a301_game_server/pkg/logger" + "a301_game_server/pkg/mathutil" + pb "a301_game_server/proto/gen/pb" +) + +const defaultZoneID uint32 = 1 + +// GameServer is the top-level orchestrator that connects networking with game logic. +type GameServer struct { + cfg *config.Config + world *World + sessions *player.SessionManager + dbPool *db.Pool + authSvc *auth.Service + charRepo *repository.CharacterRepo + + mu sync.RWMutex + connPlayer map[uint64]*player.Player // connID -> player + playerConn map[uint64]uint64 // playerID -> connID + + nextPlayerID atomic.Uint64 + cancelSave context.CancelFunc +} + +// NewGameServer creates the game server. +func NewGameServer(cfg *config.Config, dbPool *db.Pool) *GameServer { + gs := &GameServer{ + cfg: cfg, + world: NewWorld(cfg), + sessions: player.NewSessionManager(), + dbPool: dbPool, + authSvc: auth.NewService(dbPool), + charRepo: repository.NewCharacterRepo(dbPool), + connPlayer: make(map[uint64]*player.Player), + playerConn: make(map[uint64]uint64), + } + + // Create zones, portals, and mobs. + gs.setupWorld() + + return gs +} + +// World returns the game world. +func (gs *GameServer) World() *World { return gs.world } + +// Start launches all zone game loops and periodic save. +func (gs *GameServer) Start() { + gs.world.mu.RLock() + for _, zone := range gs.world.zones { + zone.SetMessageHandler(gs) + zone.SetZoneTransferCallback(gs.handleZoneTransfer) + } + gs.world.mu.RUnlock() + + gs.world.StartAll() + + // Start periodic character save. + ctx, cancel := context.WithCancel(context.Background()) + gs.cancelSave = cancel + go gs.periodicSave(ctx) +} + +// Stop shuts down all zone game loops and saves all players. +func (gs *GameServer) Stop() { + if gs.cancelSave != nil { + gs.cancelSave() + } + + // Final save of all online players. + gs.saveAllPlayers() + + gs.world.StopAll() +} + +// OnPacket handles incoming packets from a connection. +func (gs *GameServer) OnPacket(conn *network.Connection, pkt *network.Packet) { + switch pkt.Type { + case network.MsgLoginRequest: + gs.handleLogin(conn, pkt) + case network.MsgEnterWorldRequest: + gs.handleEnterWorld(conn, pkt) + default: + gs.mu.RLock() + p, ok := gs.connPlayer[conn.ID()] + gs.mu.RUnlock() + if !ok { + return + } + + zone, err := gs.world.GetZone(p.ZoneID()) + if err != nil { + return + } + zone.EnqueueMessage(PlayerMessage{PlayerID: p.EntityID(), Packet: pkt}) + } +} + +// OnDisconnect handles a connection closing. +func (gs *GameServer) OnDisconnect(conn *network.Connection) { + gs.mu.Lock() + p, ok := gs.connPlayer[conn.ID()] + if !ok { + gs.mu.Unlock() + return + } + delete(gs.connPlayer, conn.ID()) + delete(gs.playerConn, p.EntityID()) + gs.mu.Unlock() + + // Save character to DB on disconnect. + if p.CharID() != 0 { + if err := gs.charRepo.Save(context.Background(), p.ToCharacterData()); err != nil { + logger.Error("failed to save player on disconnect", "playerID", p.EntityID(), "error", err) + } + } + + zone, err := gs.world.GetZone(p.ZoneID()) + if err == nil { + zone.EnqueueMessage(PlayerMessage{ + PlayerID: p.EntityID(), + Packet: &network.Packet{Type: msgPlayerDisconnect}, + }) + } + + logger.Info("player disconnected", "connID", conn.ID(), "playerID", p.EntityID()) +} + +// Internal message types. +const ( + msgPlayerDisconnect uint16 = 0xFFFF + msgPlayerEnterWorld uint16 = 0xFFFE +) + +// HandleZoneMessage implements ZoneMessageHandler. +func (gs *GameServer) HandleZoneMessage(zone *Zone, msg PlayerMessage) bool { + switch msg.Packet.Type { + case msgPlayerDisconnect: + zone.RemovePlayer(msg.PlayerID) + return true + case msgPlayerEnterWorld: + gs.mu.RLock() + var found *player.Player + for _, p := range gs.connPlayer { + if p.EntityID() == msg.PlayerID { + found = p + break + } + } + gs.mu.RUnlock() + if found != nil { + zone.AddPlayer(found) + } + return true + default: + return false + } +} + +func (gs *GameServer) handleLogin(conn *network.Connection, pkt *network.Packet) { + req := pkt.Payload.(*pb.LoginRequest) + ctx := context.Background() + + // Try login first. + accountID, err := gs.authSvc.Login(ctx, req.Username, req.Password) + if err != nil { + // Auto-register if account doesn't exist. + accountID, err = gs.authSvc.Register(ctx, req.Username, req.Password) + if err != nil { + conn.Send(network.MsgLoginResponse, &pb.LoginResponse{ + Success: false, + ErrorMessage: "login failed: " + err.Error(), + }) + return + } + + // Create default character on first registration. + _, charErr := gs.charRepo.Create(ctx, accountID, req.Username) + if charErr != nil { + logger.Error("failed to create default character", "accountID", accountID, "error", charErr) + } + } + + session := gs.sessions.Create(uint64(accountID), req.Username) + + conn.Send(network.MsgLoginResponse, &pb.LoginResponse{ + Success: true, + SessionToken: session.Token, + PlayerId: uint64(accountID), + }) + + logger.Info("player logged in", "username", req.Username, "accountID", accountID) +} + +func (gs *GameServer) handleEnterWorld(conn *network.Connection, pkt *network.Packet) { + req := pkt.Payload.(*pb.EnterWorldRequest) + + session := gs.sessions.Get(req.SessionToken) + if session == nil { + conn.Send(network.MsgEnterWorldResponse, &pb.EnterWorldResponse{ + Success: false, + ErrorMessage: "invalid session", + }) + return + } + + ctx := context.Background() + + // Load character from DB. + chars, err := gs.charRepo.GetByAccountID(ctx, int64(session.PlayerID)) + if err != nil || len(chars) == 0 { + conn.Send(network.MsgEnterWorldResponse, &pb.EnterWorldResponse{ + Success: false, + ErrorMessage: "no character found", + }) + return + } + + charData := chars[0] // Use first character for now. + p := player.NewPlayerFromDB(charData, conn) + + // Register connection-player mapping. + gs.mu.Lock() + gs.connPlayer[conn.ID()] = p + gs.playerConn[p.EntityID()] = conn.ID() + gs.mu.Unlock() + + zoneID := p.ZoneID() + zone, err := gs.world.GetZone(zoneID) + if err != nil { + // Fall back to default zone. + zoneID = defaultZoneID + p.SetZoneID(defaultZoneID) + zone, _ = gs.world.GetZone(defaultZoneID) + } + + zone.EnqueueMessage(PlayerMessage{ + PlayerID: p.EntityID(), + Packet: &network.Packet{Type: msgPlayerEnterWorld}, + }) + + conn.Send(network.MsgEnterWorldResponse, &pb.EnterWorldResponse{ + Success: true, + Self: p.ToProto(), + ZoneId: zoneID, + }) + + logger.Info("player entered world", "playerID", p.EntityID(), "charID", charData.ID, "zone", zoneID) +} + +// periodicSave saves all dirty player data to DB at configured intervals. +func (gs *GameServer) periodicSave(ctx context.Context) { + ticker := time.NewTicker(gs.cfg.Database.SaveInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + gs.saveAllPlayers() + case <-ctx.Done(): + return + } + } +} + +func (gs *GameServer) saveAllPlayers() { + gs.mu.RLock() + var dirty []*player.Player + for _, p := range gs.connPlayer { + if p.IsDirty() && p.CharID() != 0 { + dirty = append(dirty, p) + } + } + gs.mu.RUnlock() + + if len(dirty) == 0 { + return + } + + chars := make([]*repository.CharacterData, 0, len(dirty)) + for _, p := range dirty { + chars = append(chars, p.ToCharacterData()) + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + if err := gs.charRepo.SaveBatch(ctx, chars); err != nil { + logger.Error("periodic save failed", "count", len(chars), "error", err) + return + } + + for _, p := range dirty { + p.ClearDirty() + } + + logger.Debug("periodic save completed", "count", len(chars)) +} + +// setupWorld creates all zones, portals, and mob spawn points. +func (gs *GameServer) setupWorld() { + zone1 := gs.world.CreateZone(1) // Starting zone - plains + zone2 := gs.world.CreateZone(2) // Forest zone - medium difficulty + zone3 := gs.world.CreateZone(3) // Volcano zone - hard + + // Set spawn positions. + zone1.spawnPos = mathutil.NewVec3(0, 0, 0) + zone2.spawnPos = mathutil.NewVec3(5, 0, 5) + zone3.spawnPos = mathutil.NewVec3(10, 0, 10) + + // Portals: Zone 1 <-> Zone 2 + zone1.AddPortal(world.ZonePortal{ + SourceZoneID: 1, + TriggerPos: mathutil.NewVec3(300, 0, 150), + TriggerRadius: 5.0, + TargetZoneID: 2, + TargetPos: mathutil.NewVec3(5, 0, 5), + }) + zone2.AddPortal(world.ZonePortal{ + SourceZoneID: 2, + TriggerPos: mathutil.NewVec3(0, 0, 0), + TriggerRadius: 5.0, + TargetZoneID: 1, + TargetPos: mathutil.NewVec3(295, 0, 150), + }) + + // Portals: Zone 2 <-> Zone 3 + zone2.AddPortal(world.ZonePortal{ + SourceZoneID: 2, + TriggerPos: mathutil.NewVec3(300, 0, 300), + TriggerRadius: 5.0, + TargetZoneID: 3, + TargetPos: mathutil.NewVec3(10, 0, 10), + }) + zone3.AddPortal(world.ZonePortal{ + SourceZoneID: 3, + TriggerPos: mathutil.NewVec3(0, 0, 0), + TriggerRadius: 5.0, + TargetZoneID: 2, + TargetPos: mathutil.NewVec3(295, 0, 295), + }) + + // Populate zones with mobs. + gs.setupZoneMobs(zone1, []mobSpawnConfig{ + {mobID: 1, count: 3, baseX: 20, baseZ: 30, spacing: 15}, // Goblins + {mobID: 2, count: 2, baseX: 80, baseZ: 80, spacing: 12}, // Wolves + }) + gs.setupZoneMobs(zone2, []mobSpawnConfig{ + {mobID: 2, count: 4, baseX: 50, baseZ: 50, spacing: 15}, // Wolves + {mobID: 3, count: 2, baseX: 150, baseZ: 150, spacing: 20}, // Trolls + {mobID: 4, count: 1, baseX: 200, baseZ: 50, spacing: 0}, // Fire Elemental + }) + gs.setupZoneMobs(zone3, []mobSpawnConfig{ + {mobID: 4, count: 3, baseX: 80, baseZ: 80, spacing: 25}, // Fire Elementals + {mobID: 5, count: 1, baseX: 200, baseZ: 200, spacing: 0}, // Dragon Whelp + }) +} + +type mobSpawnConfig struct { + mobID uint32 + count int + baseX float32 + baseZ float32 + spacing float32 +} + +// setupZoneMobs configures mob spawn points for a zone. +func (gs *GameServer) setupZoneMobs(zone *Zone, configs []mobSpawnConfig) { + registry := ai.NewMobRegistry() + spawner := zone.Spawner() + + for _, cfg := range configs { + def := registry.Get(cfg.mobID) + if def == nil { + continue + } + + respawn := time.Duration(15+def.Level*3) * time.Second + + for i := 0; i < cfg.count; i++ { + spawner.AddSpawnPoint(ai.SpawnPoint{ + MobDef: def, + Position: mathutil.NewVec3(cfg.baseX+float32(i)*cfg.spacing, 0, cfg.baseZ+float32(i)*cfg.spacing), + RespawnDelay: respawn, + MaxCount: 1, + }) + } + } + + spawner.InitialSpawn() +} + +// handleZoneTransfer moves a player between zones. +func (gs *GameServer) handleZoneTransfer(playerID uint64, targetZoneID uint32, targetPos mathutil.Vec3) { + gs.mu.RLock() + var p *player.Player + var connID uint64 + for cid, pl := range gs.connPlayer { + if pl.EntityID() == playerID { + p = pl + connID = cid + break + } + } + gs.mu.RUnlock() + + if p == nil { + return + } + _ = connID + + sourceZone, err := gs.world.GetZone(p.ZoneID()) + if err != nil { + return + } + targetZone, err := gs.world.GetZone(targetZoneID) + if err != nil { + logger.Warn("zone transfer target not found", "targetZone", targetZoneID) + return + } + + // Remove from source zone. + sourceZone.RemovePlayer(playerID) + + // Update player state. + p.SetPosition(targetPos) + p.SetZoneID(targetZoneID) + + // Add to target zone via message queue. + targetZone.EnqueueMessage(PlayerMessage{ + PlayerID: playerID, + Packet: &network.Packet{Type: msgPlayerEnterWorld}, + }) + + // Notify client of zone change. + p.Connection().Send(network.MsgZoneTransferNotify, &pb.ZoneTransferNotify{ + NewZoneId: targetZoneID, + Self: p.ToProto(), + }) + + logger.Info("zone transfer", + "playerID", playerID, + "from", sourceZone.ID(), + "to", targetZoneID, + ) +} diff --git a/internal/game/world.go b/internal/game/world.go new file mode 100644 index 0000000..d69d28b --- /dev/null +++ b/internal/game/world.go @@ -0,0 +1,94 @@ +package game + +import ( + "fmt" + "sync" + + "a301_game_server/config" + "a301_game_server/pkg/logger" +) + +// World manages all zones. +type World struct { + mu sync.RWMutex + zones map[uint32]*Zone + cfg *config.Config +} + +// NewWorld creates a new world. +func NewWorld(cfg *config.Config) *World { + return &World{ + zones: make(map[uint32]*Zone), + cfg: cfg, + } +} + +// CreateZone creates and registers a new zone. +func (w *World) CreateZone(id uint32) *Zone { + zone := NewZone(id, w.cfg) + + w.mu.Lock() + w.zones[id] = zone + w.mu.Unlock() + + logger.Info("zone created", "zoneID", id) + return zone +} + +// GetZone returns a zone by ID. +func (w *World) GetZone(id uint32) (*Zone, error) { + w.mu.RLock() + defer w.mu.RUnlock() + + zone, ok := w.zones[id] + if !ok { + return nil, fmt.Errorf("zone %d not found", id) + } + return zone, nil +} + +// StartAll launches all zone game loops. +func (w *World) StartAll() { + w.mu.RLock() + defer w.mu.RUnlock() + + for _, zone := range w.zones { + go zone.Run() + } + logger.Info("all zones started", "count", len(w.zones)) +} + +// StopAll stops all zone game loops. +func (w *World) StopAll() { + w.mu.RLock() + defer w.mu.RUnlock() + + for _, zone := range w.zones { + zone.Stop() + } + logger.Info("all zones stopped") +} + +// TotalPlayers returns the total number of online players across all zones. +func (w *World) TotalPlayers() int { + w.mu.RLock() + defer w.mu.RUnlock() + + total := 0 + for _, zone := range w.zones { + total += zone.PlayerCount() + } + return total +} + +// TotalEntities returns the total number of entities across all zones. +func (w *World) TotalEntities() int { + w.mu.RLock() + defer w.mu.RUnlock() + + total := 0 + for _, zone := range w.zones { + total += zone.EntityCount() + } + return total +} diff --git a/internal/game/zone.go b/internal/game/zone.go new file mode 100644 index 0000000..cbdca62 --- /dev/null +++ b/internal/game/zone.go @@ -0,0 +1,665 @@ +package game + +import ( + "sync/atomic" + "time" + + "a301_game_server/config" + "a301_game_server/internal/ai" + "a301_game_server/internal/combat" + "a301_game_server/internal/entity" + "a301_game_server/internal/network" + "a301_game_server/internal/world" + "a301_game_server/pkg/logger" + "a301_game_server/pkg/mathutil" + pb "a301_game_server/proto/gen/pb" + "google.golang.org/protobuf/proto" +) + +const ( + maxMoveSpeed float32 = 10.0 // units per second +) + +// PlayerMessage is a message from a player connection queued for zone processing. +type PlayerMessage struct { + PlayerID uint64 + Packet *network.Packet +} + +// PlayerEntity wraps an entity.Entity that is also a connected player. +type PlayerEntity interface { + entity.Entity + Connection() *network.Connection + Velocity() mathutil.Vec3 + SetVelocity(vel mathutil.Vec3) +} + +// ZoneMessageHandler provides an extension point for handling custom message types in a zone. +type ZoneMessageHandler interface { + HandleZoneMessage(zone *Zone, msg PlayerMessage) bool // returns true if handled +} + +// Zone is a self-contained game area with its own game loop. +type Zone struct { + id uint32 + cfg *config.Config + entities map[uint64]entity.Entity + players map[uint64]PlayerEntity + aoi world.AOIManager + incoming chan PlayerMessage + stopCh chan struct{} + tick int64 + + // Metrics + lastTickDuration atomic.Int64 + + // AOI toggle support + aoiEnabled bool + gridAOI *world.GridAOI + broadcastAOI *world.BroadcastAllAOI + + // Combat + combatMgr *combat.Manager + + // AI / Mobs + spawner *ai.Spawner + nextEntityID atomic.Uint64 + + // External message handler for custom/internal messages. + extHandler ZoneMessageHandler + + // Respawn position + spawnPos mathutil.Vec3 + + // Zone portals + portals []world.ZonePortal + + // Zone transfer callback (set by GameServer) + onZoneTransfer func(playerID uint64, targetZoneID uint32, targetPos mathutil.Vec3) +} + +// NewZone creates a new zone with the given configuration. +func NewZone(id uint32, cfg *config.Config) *Zone { + gridAOI := world.NewGridAOI(cfg.World.AOI.CellSize, cfg.World.AOI.ViewRange) + broadcastAOI := world.NewBroadcastAllAOI() + + var activeAOI world.AOIManager + if cfg.World.AOI.Enabled { + activeAOI = gridAOI + } else { + activeAOI = broadcastAOI + } + + cm := combat.NewManager() + + z := &Zone{ + id: id, + cfg: cfg, + entities: make(map[uint64]entity.Entity), + players: make(map[uint64]PlayerEntity), + aoi: activeAOI, + incoming: make(chan PlayerMessage, 4096), + stopCh: make(chan struct{}), + aoiEnabled: cfg.World.AOI.Enabled, + gridAOI: gridAOI, + broadcastAOI: broadcastAOI, + combatMgr: cm, + spawnPos: mathutil.NewVec3(0, 0, 0), + } + + // Wire combat manager broadcast to zone AOI. + cm.SetBroadcast(z.broadcastCombatEvent, z.sendToEntity) + + // Create mob spawner. + z.spawner = ai.NewSpawner( + &z.nextEntityID, + func(m *ai.Mob) { z.addMob(m) }, + func(mobID uint64) { z.removeMob(mobID) }, + ) + + return z +} + +// ID returns the zone identifier. +func (z *Zone) ID() uint32 { return z.id } + +// AddPortal registers a zone portal. +func (z *Zone) AddPortal(portal world.ZonePortal) { + z.portals = append(z.portals, portal) +} + +// SetZoneTransferCallback sets the function called when a player enters a portal. +func (z *Zone) SetZoneTransferCallback(fn func(playerID uint64, targetZoneID uint32, targetPos mathutil.Vec3)) { + z.onZoneTransfer = fn +} + +// PlayerCount returns the current number of players in this zone. +func (z *Zone) PlayerCount() int { return len(z.players) } + +// EntityCount returns the current number of entities in this zone. +func (z *Zone) EntityCount() int { return len(z.entities) } + +// LastTickDuration returns the duration of the last tick in microseconds. +func (z *Zone) LastTickDuration() int64 { return z.lastTickDuration.Load() } + +// AOIEnabled returns whether grid-based AOI is currently active. +func (z *Zone) AOIEnabled() bool { return z.aoiEnabled } + +// EnqueueMessage queues a player message for processing in the next tick. +func (z *Zone) EnqueueMessage(msg PlayerMessage) { + select { + case z.incoming <- msg: + default: + logger.Warn("zone message queue full, dropping", "zoneID", z.id, "playerID", msg.PlayerID) + } +} + +// AddPlayer adds a player to the zone. +// Must be called from the zone's goroutine or before Run() starts. +func (z *Zone) AddPlayer(p PlayerEntity) { + z.entities[p.EntityID()] = p + z.players[p.EntityID()] = p + z.aoi.Add(p) + + // Notify existing nearby players about the new player. + spawnData, _ := network.Encode(network.MsgSpawnEntity, &pb.SpawnEntity{Entity: p.ToProto()}) + for _, nearby := range z.aoi.GetNearby(p) { + if np, ok := z.players[nearby.EntityID()]; ok { + np.Connection().SendRaw(spawnData) + } + } + + logger.Info("player added to zone", "zoneID", z.id, "playerID", p.EntityID(), "players", len(z.players)) +} + +// RemovePlayer removes a player from the zone. +func (z *Zone) RemovePlayer(playerID uint64) { + entity, ok := z.entities[playerID] + if !ok { + return + } + + events := z.aoi.Remove(entity) + z.handleAOIEvents(events) + + z.combatMgr.RemoveEntity(playerID) + delete(z.entities, playerID) + delete(z.players, playerID) + + logger.Info("player removed from zone", "zoneID", z.id, "playerID", playerID, "players", len(z.players)) +} + +// ToggleAOI switches between grid-based and broadcast-all AOI at runtime. +func (z *Zone) ToggleAOI(enabled bool) { + if z.aoiEnabled == enabled { + return + } + + z.aoiEnabled = enabled + + // Rebuild the target AOI manager with current entities. + var newAOI world.AOIManager + if enabled { + g := world.NewGridAOI(z.cfg.World.AOI.CellSize, z.cfg.World.AOI.ViewRange) + for _, e := range z.entities { + g.Add(e) + } + z.gridAOI = g + newAOI = g + } else { + b := world.NewBroadcastAllAOI() + for _, e := range z.entities { + b.Add(e) + } + z.broadcastAOI = b + newAOI = b + } + + z.aoi = newAOI + + // After toggle, send full spawn list to all players so they see the correct set. + for _, p := range z.players { + z.sendNearbySnapshot(p) + } + + logger.Info("AOI toggled", "zoneID", z.id, "enabled", enabled) +} + +// Run starts the zone's game loop. Blocks until Stop() is called. +func (z *Zone) Run() { + interval := z.cfg.TickInterval() + ticker := time.NewTicker(interval) + defer ticker.Stop() + + logger.Info("zone started", "zoneID", z.id, "tickInterval", interval) + + for { + select { + case <-ticker.C: + start := time.Now() + z.processTick() + z.lastTickDuration.Store(time.Since(start).Microseconds()) + case <-z.stopCh: + logger.Info("zone stopped", "zoneID", z.id) + return + } + } +} + +// Stop signals the zone's game loop to exit. +func (z *Zone) Stop() { + close(z.stopCh) +} + +func (z *Zone) processTick() { + z.tick++ + z.processInputQueue() + z.updateMovement() + z.updateAI() + z.updateCombat() + z.checkDeaths() + z.spawner.Update(time.Now()) + z.broadcastState() +} + +func (z *Zone) updateCombat() { + dt := z.cfg.TickInterval() + z.combatMgr.UpdateBuffs(dt, func(id uint64) combat.Combatant { + if p, ok := z.players[id]; ok { + if c, ok := p.(combat.Combatant); ok { + return c + } + } + return nil + }) +} + +func (z *Zone) processInputQueue() { + for { + select { + case msg := <-z.incoming: + z.handleMessage(msg) + default: + return + } + } +} + +// SetMessageHandler sets an external handler for custom message types. +func (z *Zone) SetMessageHandler(h ZoneMessageHandler) { + z.extHandler = h +} + +func (z *Zone) handleMessage(msg PlayerMessage) { + // Try external handler first (for internal messages like disconnect/enter). + if z.extHandler != nil && z.extHandler.HandleZoneMessage(z, msg) { + return + } + + switch msg.Packet.Type { + case network.MsgMoveRequest: + z.handleMoveRequest(msg) + case network.MsgUseSkillRequest: + z.handleUseSkill(msg) + case network.MsgRespawnRequest: + z.handleRespawn(msg) + case network.MsgPing: + z.handlePing(msg) + case network.MsgAOIToggleRequest: + z.handleAOIToggle(msg) + case network.MsgMetricsRequest: + z.handleMetrics(msg) + } +} + +func (z *Zone) handleMoveRequest(msg PlayerMessage) { + p, ok := z.players[msg.PlayerID] + if !ok { + return + } + + req := msg.Packet.Payload.(*pb.MoveRequest) + + newPos := mathutil.NewVec3(req.Position.X, req.Position.Y, req.Position.Z) + vel := mathutil.NewVec3(req.Velocity.X, req.Velocity.Y, req.Velocity.Z) + + // Server-side speed validation. + if vel.Length() > maxMoveSpeed*1.1 { // 10% tolerance + vel = vel.Normalize().Scale(maxMoveSpeed) + } + + oldPos := p.Position() + p.SetPosition(newPos) + p.SetRotation(req.Rotation) + p.SetVelocity(vel) + + // Update AOI and handle events. + events := z.aoi.UpdatePosition(p, oldPos, newPos) + z.handleAOIEvents(events) + + // Check portal triggers. + z.checkPortals(p, newPos) +} + +func (z *Zone) handlePing(msg PlayerMessage) { + p, ok := z.players[msg.PlayerID] + if !ok { + return + } + ping := msg.Packet.Payload.(*pb.Ping) + p.Connection().Send(network.MsgPong, &pb.Pong{ + ClientTime: ping.ClientTime, + ServerTime: time.Now().UnixMilli(), + }) +} + +func (z *Zone) handleAOIToggle(msg PlayerMessage) { + p, ok := z.players[msg.PlayerID] + if !ok { + return + } + req := msg.Packet.Payload.(*pb.AOIToggleRequest) + z.ToggleAOI(req.Enabled) + + status := "disabled" + if req.Enabled { + status = "enabled" + } + p.Connection().Send(network.MsgAOIToggleResponse, &pb.AOIToggleResponse{ + Enabled: req.Enabled, + Message: "AOI " + status, + }) +} + +func (z *Zone) handleMetrics(msg PlayerMessage) { + p, ok := z.players[msg.PlayerID] + if !ok { + return + } + p.Connection().Send(network.MsgServerMetrics, &pb.ServerMetrics{ + OnlinePlayers: int32(len(z.players)), + TotalEntities: int32(len(z.entities)), + TickDurationUs: z.lastTickDuration.Load(), + AoiEnabled: z.aoiEnabled, + }) +} + +func (z *Zone) updateMovement() { + // Movement is applied immediately in handleMoveRequest (client-authoritative position + // with server validation). Future: add server-side physics/collision here. +} + +func (z *Zone) broadcastState() { + if len(z.players) == 0 { + return + } + + // For each player, send state updates of nearby entities. + for _, p := range z.players { + nearby := z.aoi.GetNearby(p) + if len(nearby) == 0 { + continue + } + + states := make([]*pb.EntityState, 0, len(nearby)) + for _, e := range nearby { + states = append(states, e.ToProto()) + } + + p.Connection().Send(network.MsgStateUpdate, &pb.StateUpdate{ + Entities: states, + ServerTick: z.tick, + }) + } +} + +func (z *Zone) handleAOIEvents(events []world.AOIEvent) { + for _, evt := range events { + observerPlayer, ok := z.players[evt.Observer.EntityID()] + if !ok { + continue + } + + switch evt.Type { + case world.AOIEnter: + observerPlayer.Connection().Send(network.MsgSpawnEntity, &pb.SpawnEntity{ + Entity: evt.Target.ToProto(), + }) + case world.AOILeave: + observerPlayer.Connection().Send(network.MsgDespawnEntity, &pb.DespawnEntity{ + EntityId: evt.Target.EntityID(), + }) + } + } +} + +func (z *Zone) sendNearbySnapshot(p PlayerEntity) { + nearby := z.aoi.GetNearby(p) + states := make([]*pb.EntityState, 0, len(nearby)) + for _, e := range nearby { + states = append(states, e.ToProto()) + } + p.Connection().Send(network.MsgStateUpdate, &pb.StateUpdate{ + Entities: states, + ServerTick: z.tick, + }) +} + +// ─── Combat Handlers ──────────────────────────────────────── + +func (z *Zone) handleUseSkill(msg PlayerMessage) { + p, ok := z.players[msg.PlayerID] + if !ok { + return + } + + req := msg.Packet.Payload.(*pb.UseSkillRequest) + + var targetPos mathutil.Vec3 + if req.TargetPos != nil { + targetPos = mathutil.NewVec3(req.TargetPos.X, req.TargetPos.Y, req.TargetPos.Z) + } + + caster, ok := p.(combat.Combatant) + if !ok { + return + } + + success, errMsg := z.combatMgr.UseSkill( + caster, + req.SkillId, + req.TargetId, + targetPos, + func(id uint64) entity.Entity { return z.entities[id] }, + z.getEntitiesInRadius, + ) + + p.Connection().Send(network.MsgUseSkillResponse, &pb.UseSkillResponse{ + Success: success, + ErrorMessage: errMsg, + }) +} + +func (z *Zone) handleRespawn(msg PlayerMessage) { + p, ok := z.players[msg.PlayerID] + if !ok { + return + } + + c, ok := p.(combat.Combatant) + if !ok || c.IsAlive() { + return + } + + oldPos := p.Position() + z.combatMgr.Respawn(c, z.spawnPos) + + // Update AOI for the new position. + events := z.aoi.UpdatePosition(p, oldPos, z.spawnPos) + z.handleAOIEvents(events) + + // Notify respawn. + respawnEvt := &pb.CombatEvent{ + TargetId: p.EntityID(), + TargetHp: c.HP(), + TargetMaxHp: c.MaxHP(), + EventType: pb.CombatEventType_COMBAT_EVENT_RESPAWN, + } + z.broadcastCombatEvent(p, network.MsgCombatEvent, respawnEvt) + + p.Connection().Send(network.MsgRespawnResponse, &pb.RespawnResponse{ + Self: p.ToProto(), + }) +} + +// broadcastCombatEvent sends a combat event to all players who can see the entity. +func (z *Zone) broadcastCombatEvent(ent entity.Entity, msgType uint16, msg interface{}) { + protoMsg, ok := msg.(proto.Message) + if !ok { + return + } + data, err := network.Encode(msgType, protoMsg) + if err != nil { + return + } + + // Send to the entity itself if it's a player. + if p, ok := z.players[ent.EntityID()]; ok { + p.Connection().SendRaw(data) + } + + // Send to nearby players. + for _, nearby := range z.aoi.GetNearby(ent) { + if p, ok := z.players[nearby.EntityID()]; ok { + p.Connection().SendRaw(data) + } + } +} + +// sendToEntity sends a message to a specific entity (if it's a player). +func (z *Zone) sendToEntity(entityID uint64, msgType uint16, msg interface{}) { + p, ok := z.players[entityID] + if !ok { + return + } + protoMsg, ok := msg.(proto.Message) + if !ok { + return + } + p.Connection().Send(msgType, protoMsg) +} + +// getEntitiesInRadius returns all entities within a radius of a point. +func (z *Zone) getEntitiesInRadius(center mathutil.Vec3, radius float32) []entity.Entity { + radiusSq := radius * radius + var result []entity.Entity + for _, e := range z.entities { + if e.Position().DistanceSqTo(center) <= radiusSq { + result = append(result, e) + } + } + return result +} + +// ─── AI / Mob Management ──────────────────────────────────── + +// Spawner returns the zone's mob spawner for external configuration. +func (z *Zone) Spawner() *ai.Spawner { return z.spawner } + +func (z *Zone) addMob(m *ai.Mob) { + z.entities[m.EntityID()] = m + z.aoi.Add(m) + + // Notify nearby players about the new mob. + spawnData, _ := network.Encode(network.MsgSpawnEntity, &pb.SpawnEntity{Entity: m.ToProto()}) + for _, nearby := range z.aoi.GetNearby(m) { + if p, ok := z.players[nearby.EntityID()]; ok { + p.Connection().SendRaw(spawnData) + } + } +} + +func (z *Zone) removeMob(mobID uint64) { + ent, ok := z.entities[mobID] + if !ok { + return + } + + events := z.aoi.Remove(ent) + z.handleAOIEvents(events) + z.combatMgr.RemoveEntity(mobID) + delete(z.entities, mobID) +} + +func (z *Zone) updateAI() { + dt := z.cfg.TickInterval() + for _, m := range z.spawner.AliveMobs() { + oldPos := m.Position() + ai.UpdateMob(m, dt, z, z) + newPos := m.Position() + + // Update AOI if mob moved. + if oldPos != newPos { + events := z.aoi.UpdatePosition(m, oldPos, newPos) + z.handleAOIEvents(events) + } + } +} + +func (z *Zone) checkDeaths() { + for _, m := range z.spawner.AliveMobs() { + if !m.IsAlive() { + z.spawner.NotifyDeath(m.EntityID()) + } + } +} + +// ─── ai.EntityProvider implementation ─────────────────────── + +func (z *Zone) GetEntity(id uint64) entity.Entity { + return z.entities[id] +} + +func (z *Zone) GetPlayersInRange(center mathutil.Vec3, radius float32) []entity.Entity { + radiusSq := radius * radius + var result []entity.Entity + for _, p := range z.players { + if p.Position().DistanceSqTo(center) <= radiusSq { + result = append(result, p) + } + } + return result +} + +// ─── ai.SkillUser implementation ──────────────────────────── + +func (z *Zone) UseSkill(casterID uint64, skillID uint32, targetID uint64, targetPos mathutil.Vec3) (bool, string) { + ent := z.entities[casterID] + if ent == nil { + return false, "caster not found" + } + caster, ok := ent.(combat.Combatant) + if !ok { + return false, "caster cannot fight" + } + + return z.combatMgr.UseSkill( + caster, skillID, targetID, targetPos, + func(id uint64) entity.Entity { return z.entities[id] }, + z.getEntitiesInRadius, + ) +} + +// ─── Zone Portals ─────────────────────────────────────────── + +func (z *Zone) checkPortals(p PlayerEntity, pos mathutil.Vec3) { + if z.onZoneTransfer == nil || len(z.portals) == 0 { + return + } + for _, portal := range z.portals { + if portal.IsInRange(pos) { + z.onZoneTransfer(p.EntityID(), portal.TargetZoneID, portal.TargetPos) + return + } + } +} diff --git a/internal/network/connection.go b/internal/network/connection.go new file mode 100644 index 0000000..19df558 --- /dev/null +++ b/internal/network/connection.go @@ -0,0 +1,174 @@ +package network + +import ( + "sync" + "sync/atomic" + "time" + + "github.com/gorilla/websocket" + "google.golang.org/protobuf/proto" + + "a301_game_server/pkg/logger" +) + +// ConnState represents the lifecycle state of a connection. +type ConnState int32 + +const ( + ConnStateActive ConnState = iota + ConnStateClosed +) + +// Connection wraps a WebSocket connection with send buffering and lifecycle management. +type Connection struct { + id uint64 + ws *websocket.Conn + sendCh chan []byte + handler PacketHandler + state atomic.Int32 + closeOnce sync.Once + + maxMessageSize int64 + heartbeatInterval time.Duration + heartbeatTimeout time.Duration +} + +// PacketHandler processes incoming packets from a connection. +type PacketHandler interface { + OnPacket(conn *Connection, pkt *Packet) + OnDisconnect(conn *Connection) +} + +// NewConnection creates a new Connection wrapping the given WebSocket. +func NewConnection(id uint64, ws *websocket.Conn, handler PacketHandler, sendChSize int, maxMsgSize int64, hbInterval, hbTimeout time.Duration) *Connection { + c := &Connection{ + id: id, + ws: ws, + sendCh: make(chan []byte, sendChSize), + handler: handler, + maxMessageSize: maxMsgSize, + heartbeatInterval: hbInterval, + heartbeatTimeout: hbTimeout, + } + c.state.Store(int32(ConnStateActive)) + return c +} + +// ID returns the connection's unique identifier. +func (c *Connection) ID() uint64 { return c.id } + +// Start launches the read and write goroutines. +func (c *Connection) Start() { + go c.readLoop() + go c.writeLoop() +} + +// Send encodes and queues a message for sending. Non-blocking: drops if buffer is full. +func (c *Connection) Send(msgType uint16, msg proto.Message) { + if c.IsClosed() { + return + } + + data, err := Encode(msgType, msg) + if err != nil { + logger.Error("encode failed", "connID", c.id, "msgType", msgType, "error", err) + return + } + + select { + case c.sendCh <- data: + default: + logger.Warn("send buffer full, dropping message", "connID", c.id, "msgType", msgType) + } +} + +// SendRaw queues pre-encoded data for sending. Non-blocking. +func (c *Connection) SendRaw(data []byte) { + if c.IsClosed() { + return + } + select { + case c.sendCh <- data: + default: + logger.Warn("send buffer full, dropping raw message", "connID", c.id) + } +} + +// Close terminates the connection. +func (c *Connection) Close() { + c.closeOnce.Do(func() { + c.state.Store(int32(ConnStateClosed)) + close(c.sendCh) + _ = c.ws.Close() + }) +} + +// IsClosed returns true if the connection has been closed. +func (c *Connection) IsClosed() bool { + return ConnState(c.state.Load()) == ConnStateClosed +} + +func (c *Connection) readLoop() { + defer func() { + c.handler.OnDisconnect(c) + c.Close() + }() + + c.ws.SetReadLimit(c.maxMessageSize) + _ = c.ws.SetReadDeadline(time.Now().Add(c.heartbeatTimeout)) + + c.ws.SetPongHandler(func(string) error { + _ = c.ws.SetReadDeadline(time.Now().Add(c.heartbeatTimeout)) + return nil + }) + + for { + msgType, data, err := c.ws.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) { + logger.Debug("read error", "connID", c.id, "error", err) + } + return + } + + if msgType != websocket.BinaryMessage { + continue + } + + pkt, err := Decode(data) + if err != nil { + logger.Warn("decode error", "connID", c.id, "error", err) + continue + } + + c.handler.OnPacket(c, pkt) + } +} + +func (c *Connection) writeLoop() { + ticker := time.NewTicker(c.heartbeatInterval) + defer ticker.Stop() + + for { + select { + case data, ok := <-c.sendCh: + if !ok { + _ = c.ws.WriteMessage(websocket.CloseMessage, + websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) + return + } + + _ = c.ws.SetWriteDeadline(time.Now().Add(10 * time.Second)) + if err := c.ws.WriteMessage(websocket.BinaryMessage, data); err != nil { + logger.Debug("write error", "connID", c.id, "error", err) + return + } + + case <-ticker.C: + _ = c.ws.SetWriteDeadline(time.Now().Add(10 * time.Second)) + if err := c.ws.WriteMessage(websocket.PingMessage, nil); err != nil { + return + } + } + } +} diff --git a/internal/network/packet.go b/internal/network/packet.go new file mode 100644 index 0000000..6e2ccb3 --- /dev/null +++ b/internal/network/packet.go @@ -0,0 +1,121 @@ +package network + +import ( + "encoding/binary" + "errors" + "fmt" + + "google.golang.org/protobuf/proto" + + pb "a301_game_server/proto/gen/pb" +) + +// Message type IDs — the wire protocol uses 2-byte type prefixes. +const ( + // Auth + MsgLoginRequest uint16 = 0x0001 + MsgLoginResponse uint16 = 0x0002 + MsgEnterWorldRequest uint16 = 0x0003 + MsgEnterWorldResponse uint16 = 0x0004 + + // Movement + MsgMoveRequest uint16 = 0x0010 + MsgStateUpdate uint16 = 0x0011 + MsgSpawnEntity uint16 = 0x0012 + MsgDespawnEntity uint16 = 0x0013 + + // Zone Transfer + MsgZoneTransferNotify uint16 = 0x0014 + + // System + MsgPing uint16 = 0x0020 + MsgPong uint16 = 0x0021 + + // Combat + MsgUseSkillRequest uint16 = 0x0040 + MsgUseSkillResponse uint16 = 0x0041 + MsgCombatEvent uint16 = 0x0042 + MsgBuffApplied uint16 = 0x0043 + MsgBuffRemoved uint16 = 0x0044 + MsgRespawnRequest uint16 = 0x0045 + MsgRespawnResponse uint16 = 0x0046 + + // Admin / Debug + MsgAOIToggleRequest uint16 = 0x0030 + MsgAOIToggleResponse uint16 = 0x0031 + MsgMetricsRequest uint16 = 0x0032 + MsgServerMetrics uint16 = 0x0033 +) + +var ( + ErrUnknownMessageType = errors.New("unknown message type") + ErrMessageTooShort = errors.New("message too short") +) + +// Packet is a decoded network message. +type Packet struct { + Type uint16 + Payload proto.Message +} + +// messageFactory maps type IDs to protobuf message constructors. +var messageFactory = map[uint16]func() proto.Message{ + MsgLoginRequest: func() proto.Message { return &pb.LoginRequest{} }, + MsgLoginResponse: func() proto.Message { return &pb.LoginResponse{} }, + MsgEnterWorldRequest: func() proto.Message { return &pb.EnterWorldRequest{} }, + MsgEnterWorldResponse: func() proto.Message { return &pb.EnterWorldResponse{} }, + MsgMoveRequest: func() proto.Message { return &pb.MoveRequest{} }, + MsgStateUpdate: func() proto.Message { return &pb.StateUpdate{} }, + MsgSpawnEntity: func() proto.Message { return &pb.SpawnEntity{} }, + MsgDespawnEntity: func() proto.Message { return &pb.DespawnEntity{} }, + MsgPing: func() proto.Message { return &pb.Ping{} }, + MsgPong: func() proto.Message { return &pb.Pong{} }, + MsgZoneTransferNotify: func() proto.Message { return &pb.ZoneTransferNotify{} }, + MsgUseSkillRequest: func() proto.Message { return &pb.UseSkillRequest{} }, + MsgUseSkillResponse: func() proto.Message { return &pb.UseSkillResponse{} }, + MsgCombatEvent: func() proto.Message { return &pb.CombatEvent{} }, + MsgBuffApplied: func() proto.Message { return &pb.BuffApplied{} }, + MsgBuffRemoved: func() proto.Message { return &pb.BuffRemoved{} }, + MsgRespawnRequest: func() proto.Message { return &pb.RespawnRequest{} }, + MsgRespawnResponse: func() proto.Message { return &pb.RespawnResponse{} }, + MsgAOIToggleRequest: func() proto.Message { return &pb.AOIToggleRequest{} }, + MsgAOIToggleResponse: func() proto.Message { return &pb.AOIToggleResponse{} }, + MsgMetricsRequest: func() proto.Message { return &pb.MetricsRequest{} }, + MsgServerMetrics: func() proto.Message { return &pb.ServerMetrics{} }, +} + +// Encode serializes a packet into a wire-format byte slice: [2-byte type][protobuf payload]. +func Encode(msgType uint16, msg proto.Message) ([]byte, error) { + payload, err := proto.Marshal(msg) + if err != nil { + return nil, fmt.Errorf("marshal message 0x%04X: %w", msgType, err) + } + + buf := make([]byte, 2+len(payload)) + binary.BigEndian.PutUint16(buf[:2], msgType) + copy(buf[2:], payload) + return buf, nil +} + +// Decode parses a wire-format byte slice into a Packet. +func Decode(data []byte) (*Packet, error) { + if len(data) < 2 { + return nil, ErrMessageTooShort + } + + msgType := binary.BigEndian.Uint16(data[:2]) + + factory, ok := messageFactory[msgType] + if !ok { + return nil, fmt.Errorf("%w: 0x%04X", ErrUnknownMessageType, msgType) + } + + msg := factory() + if len(data) > 2 { + if err := proto.Unmarshal(data[2:], msg); err != nil { + return nil, fmt.Errorf("unmarshal message 0x%04X: %w", msgType, err) + } + } + + return &Packet{Type: msgType, Payload: msg}, nil +} diff --git a/internal/network/server.go b/internal/network/server.go new file mode 100644 index 0000000..8016a40 --- /dev/null +++ b/internal/network/server.go @@ -0,0 +1,89 @@ +package network + +import ( + "context" + "fmt" + "net/http" + "sync/atomic" + + "github.com/gorilla/websocket" + + "a301_game_server/config" + "a301_game_server/pkg/logger" +) + +// Server listens for WebSocket connections and creates Connection objects. +type Server struct { + cfg *config.Config + upgrader websocket.Upgrader + handler PacketHandler + nextID atomic.Uint64 + srv *http.Server +} + +// NewServer creates a new WebSocket server. +func NewServer(cfg *config.Config, handler PacketHandler) *Server { + return &Server{ + cfg: cfg, + handler: handler, + upgrader: websocket.Upgrader{ + ReadBufferSize: cfg.Network.ReadBufferSize, + WriteBufferSize: cfg.Network.WriteBufferSize, + CheckOrigin: func(r *http.Request) bool { return true }, + }, + } +} + +// Start begins listening for connections. Blocks until the context is cancelled. +func (s *Server) Start(ctx context.Context) error { + mux := http.NewServeMux() + mux.HandleFunc("/ws", s.handleWebSocket) + mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ok")) + }) + + addr := s.cfg.Server.Address() + s.srv = &http.Server{ + Addr: addr, + Handler: mux, + } + + errCh := make(chan error, 1) + go func() { + logger.Info("websocket server starting", "address", addr) + if err := s.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + errCh <- fmt.Errorf("listen: %w", err) + } + }() + + select { + case err := <-errCh: + return err + case <-ctx.Done(): + logger.Info("shutting down websocket server") + return s.srv.Shutdown(context.Background()) + } +} + +func (s *Server) handleWebSocket(w http.ResponseWriter, r *http.Request) { + ws, err := s.upgrader.Upgrade(w, r, nil) + if err != nil { + logger.Error("websocket upgrade failed", "error", err, "remote", r.RemoteAddr) + return + } + + connID := s.nextID.Add(1) + conn := NewConnection( + connID, + ws, + s.handler, + s.cfg.Network.SendChannelSize, + s.cfg.Network.MaxMessageSize, + s.cfg.Network.HeartbeatInterval, + s.cfg.Network.HeartbeatTimeout, + ) + + logger.Info("client connected", "connID", connID, "remote", r.RemoteAddr) + conn.Start() +} diff --git a/internal/player/player.go b/internal/player/player.go new file mode 100644 index 0000000..0c6d3e5 --- /dev/null +++ b/internal/player/player.go @@ -0,0 +1,249 @@ +package player + +import ( + "sync" + + "a301_game_server/internal/combat" + "a301_game_server/internal/db/repository" + "a301_game_server/internal/entity" + "a301_game_server/internal/network" + "a301_game_server/pkg/mathutil" + pb "a301_game_server/proto/gen/pb" +) + +// Player represents an online player in the game world. +type Player struct { + mu sync.RWMutex + + id uint64 + charID int64 // database character ID + acctID int64 // database account ID + name string + position mathutil.Vec3 + rotation float32 + velocity mathutil.Vec3 + + stats Stats + + conn *network.Connection + zoneID uint32 + session *Session + dirty bool // true if state changed since last DB save +} + +// NewPlayer creates a new player. +func NewPlayer(id uint64, name string, conn *network.Connection) *Player { + return &Player{ + id: id, + name: name, + conn: conn, + stats: Stats{ + HP: 100, MaxHP: 100, + MP: 50, MaxMP: 50, + Str: 10, Dex: 10, Int: 10, + Level: 1, + }, + } +} + +// NewPlayerFromDB creates a player from persisted character data. +func NewPlayerFromDB(data *repository.CharacterData, conn *network.Connection) *Player { + return &Player{ + id: uint64(data.ID), + charID: data.ID, + acctID: data.AccountID, + name: data.Name, + position: mathutil.NewVec3(data.PosX, data.PosY, data.PosZ), + rotation: data.Rotation, + stats: Stats{ + HP: data.HP, MaxHP: data.MaxHP, + MP: data.MP, MaxMP: data.MaxMP, + Str: data.Str, Dex: data.Dex, Int: data.IntStat, + Level: data.Level, Exp: data.Exp, + }, + conn: conn, + zoneID: uint32(data.ZoneID), + } +} + +// ToCharacterData converts current state to a persistable format. +func (p *Player) ToCharacterData() *repository.CharacterData { + p.mu.RLock() + defer p.mu.RUnlock() + return &repository.CharacterData{ + ID: p.charID, + AccountID: p.acctID, + Name: p.name, + Level: p.stats.Level, + Exp: p.stats.Exp, + HP: p.stats.HP, + MaxHP: p.stats.MaxHP, + MP: p.stats.MP, + MaxMP: p.stats.MaxMP, + Str: p.stats.Str, + Dex: p.stats.Dex, + IntStat: p.stats.Int, + ZoneID: int32(p.zoneID), + PosX: p.position.X, + PosY: p.position.Y, + PosZ: p.position.Z, + Rotation: p.rotation, + } +} + +func (p *Player) EntityID() uint64 { return p.id } +func (p *Player) EntityType() entity.Type { return entity.TypePlayer } + +func (p *Player) Position() mathutil.Vec3 { + p.mu.RLock() + defer p.mu.RUnlock() + return p.position +} + +func (p *Player) SetPosition(pos mathutil.Vec3) { + p.mu.Lock() + defer p.mu.Unlock() + p.position = pos + p.dirty = true +} + +func (p *Player) Rotation() float32 { + p.mu.RLock() + defer p.mu.RUnlock() + return p.rotation +} + +func (p *Player) SetRotation(rot float32) { + p.mu.Lock() + defer p.mu.Unlock() + p.rotation = rot + p.dirty = true +} + +func (p *Player) Velocity() mathutil.Vec3 { + p.mu.RLock() + defer p.mu.RUnlock() + return p.velocity +} + +func (p *Player) SetVelocity(vel mathutil.Vec3) { + p.mu.Lock() + defer p.mu.Unlock() + p.velocity = vel +} + +func (p *Player) Name() string { return p.name } +func (p *Player) CharID() int64 { return p.charID } +func (p *Player) AccountID() int64 { return p.acctID } +func (p *Player) Connection() *network.Connection { return p.conn } +func (p *Player) ZoneID() uint32 { return p.zoneID } +func (p *Player) SetZoneID(id uint32) { p.zoneID = id } + +func (p *Player) Stats() combat.CombatStats { + p.mu.RLock() + defer p.mu.RUnlock() + return combat.CombatStats{ + Str: p.stats.Str, + Dex: p.stats.Dex, + Int: p.stats.Int, + Level: p.stats.Level, + } +} + +func (p *Player) RawStats() Stats { + p.mu.RLock() + defer p.mu.RUnlock() + return p.stats +} + +func (p *Player) SetStats(s Stats) { + p.mu.Lock() + defer p.mu.Unlock() + p.stats = s + p.dirty = true +} + +func (p *Player) HP() int32 { + p.mu.RLock() + defer p.mu.RUnlock() + return p.stats.HP +} + +func (p *Player) SetHP(hp int32) { + p.mu.Lock() + defer p.mu.Unlock() + if hp < 0 { + hp = 0 + } + if hp > p.stats.MaxHP { + hp = p.stats.MaxHP + } + p.stats.HP = hp + p.dirty = true +} + +func (p *Player) MaxHP() int32 { + p.mu.RLock() + defer p.mu.RUnlock() + return p.stats.MaxHP +} + +func (p *Player) MP() int32 { + p.mu.RLock() + defer p.mu.RUnlock() + return p.stats.MP +} + +func (p *Player) SetMP(mp int32) { + p.mu.Lock() + defer p.mu.Unlock() + if mp < 0 { + mp = 0 + } + if mp > p.stats.MaxMP { + mp = p.stats.MaxMP + } + p.stats.MP = mp + p.dirty = true +} + +func (p *Player) Level() int32 { + p.mu.RLock() + defer p.mu.RUnlock() + return p.stats.Level +} + +func (p *Player) IsAlive() bool { + p.mu.RLock() + defer p.mu.RUnlock() + return p.stats.HP > 0 +} + +// IsDirty returns true if state has changed since last save. +func (p *Player) IsDirty() bool { + p.mu.RLock() + defer p.mu.RUnlock() + return p.dirty +} + +// ClearDirty resets the dirty flag after a successful save. +func (p *Player) ClearDirty() { + p.mu.Lock() + defer p.mu.Unlock() + p.dirty = false +} + +func (p *Player) ToProto() *pb.EntityState { + p.mu.RLock() + defer p.mu.RUnlock() + return &pb.EntityState{ + EntityId: p.id, + Name: p.name, + Position: &pb.Vector3{X: p.position.X, Y: p.position.Y, Z: p.position.Z}, + Rotation: p.rotation, + Hp: p.stats.HP, + MaxHp: p.stats.MaxHP, + Level: p.stats.Level, + EntityType: pb.EntityType_ENTITY_TYPE_PLAYER, + } +} diff --git a/internal/player/session.go b/internal/player/session.go new file mode 100644 index 0000000..9a80b17 --- /dev/null +++ b/internal/player/session.go @@ -0,0 +1,90 @@ +package player + +import ( + "crypto/rand" + "encoding/hex" + "sync" + "time" +) + +// Session holds an authenticated player's session state. +type Session struct { + Token string + PlayerID uint64 + PlayerName string + CreatedAt time.Time + LastActive time.Time +} + +// SessionManager manages active sessions. +type SessionManager struct { + mu sync.RWMutex + sessions map[string]*Session // token -> session +} + +// NewSessionManager creates a new session manager. +func NewSessionManager() *SessionManager { + return &SessionManager{ + sessions: make(map[string]*Session), + } +} + +// Create generates a new session for the given player. +func (sm *SessionManager) Create(playerID uint64, playerName string) *Session { + token := generateToken() + now := time.Now() + + s := &Session{ + Token: token, + PlayerID: playerID, + PlayerName: playerName, + CreatedAt: now, + LastActive: now, + } + + sm.mu.Lock() + sm.sessions[token] = s + sm.mu.Unlock() + + return s +} + +// Get retrieves a session by token. Returns nil if not found. +func (sm *SessionManager) Get(token string) *Session { + sm.mu.RLock() + defer sm.mu.RUnlock() + s := sm.sessions[token] + if s != nil { + s.LastActive = time.Now() + } + return s +} + +// Remove deletes a session. +func (sm *SessionManager) Remove(token string) { + sm.mu.Lock() + delete(sm.sessions, token) + sm.mu.Unlock() +} + +// CleanupExpired removes sessions inactive for longer than the given duration. +func (sm *SessionManager) CleanupExpired(maxIdle time.Duration) int { + sm.mu.Lock() + defer sm.mu.Unlock() + + cutoff := time.Now().Add(-maxIdle) + removed := 0 + for token, s := range sm.sessions { + if s.LastActive.Before(cutoff) { + delete(sm.sessions, token) + removed++ + } + } + return removed +} + +func generateToken() string { + b := make([]byte, 32) + _, _ = rand.Read(b) + return hex.EncodeToString(b) +} diff --git a/internal/player/stats.go b/internal/player/stats.go new file mode 100644 index 0000000..fff41e0 --- /dev/null +++ b/internal/player/stats.go @@ -0,0 +1,14 @@ +package player + +// Stats holds combat-related attributes. +type Stats struct { + HP int32 + MaxHP int32 + MP int32 + MaxMP int32 + Str int32 + Dex int32 + Int int32 + Level int32 + Exp int64 +} diff --git a/internal/world/aoi.go b/internal/world/aoi.go new file mode 100644 index 0000000..82d0132 --- /dev/null +++ b/internal/world/aoi.go @@ -0,0 +1,70 @@ +package world + +import ( + "a301_game_server/internal/entity" + "a301_game_server/pkg/mathutil" +) + +// AOIEvent represents an entity entering or leaving another entity's area of interest. +type AOIEvent struct { + Observer entity.Entity + Target entity.Entity + Type AOIEventType +} + +type AOIEventType int + +const ( + AOIEnter AOIEventType = iota + AOILeave +) + +// AOIManager determines which entities can see each other. +type AOIManager interface { + Add(ent entity.Entity) + Remove(ent entity.Entity) []AOIEvent + UpdatePosition(ent entity.Entity, oldPos, newPos mathutil.Vec3) []AOIEvent + GetNearby(ent entity.Entity) []entity.Entity +} + +// BroadcastAllAOI is a trivial AOI that treats all entities as visible to each other. +// Used when AOI is disabled for debugging/comparison. +type BroadcastAllAOI struct { + entities map[uint64]entity.Entity +} + +func NewBroadcastAllAOI() *BroadcastAllAOI { + return &BroadcastAllAOI{ + entities: make(map[uint64]entity.Entity), + } +} + +func (b *BroadcastAllAOI) Add(ent entity.Entity) { + b.entities[ent.EntityID()] = ent +} + +func (b *BroadcastAllAOI) Remove(ent entity.Entity) []AOIEvent { + delete(b.entities, ent.EntityID()) + var events []AOIEvent + for _, other := range b.entities { + if other.EntityID() == ent.EntityID() { + continue + } + events = append(events, AOIEvent{Observer: other, Target: ent, Type: AOILeave}) + } + return events +} + +func (b *BroadcastAllAOI) UpdatePosition(_ entity.Entity, _, _ mathutil.Vec3) []AOIEvent { + return nil +} + +func (b *BroadcastAllAOI) GetNearby(ent entity.Entity) []entity.Entity { + result := make([]entity.Entity, 0, len(b.entities)-1) + for _, e := range b.entities { + if e.EntityID() != ent.EntityID() { + result = append(result, e) + } + } + return result +} diff --git a/internal/world/aoi_test.go b/internal/world/aoi_test.go new file mode 100644 index 0000000..f8287f0 --- /dev/null +++ b/internal/world/aoi_test.go @@ -0,0 +1,150 @@ +package world + +import ( + "testing" + + "a301_game_server/internal/entity" + "a301_game_server/pkg/mathutil" + pb "a301_game_server/proto/gen/pb" +) + +// mockEntity is a minimal entity for testing. +type mockEntity struct { + id uint64 + pos mathutil.Vec3 +} + +func (m *mockEntity) EntityID() uint64 { return m.id } +func (m *mockEntity) EntityType() entity.Type { return entity.TypePlayer } +func (m *mockEntity) Position() mathutil.Vec3 { return m.pos } +func (m *mockEntity) SetPosition(p mathutil.Vec3) { m.pos = p } +func (m *mockEntity) Rotation() float32 { return 0 } +func (m *mockEntity) SetRotation(float32) {} +func (m *mockEntity) ToProto() *pb.EntityState { return &pb.EntityState{EntityId: m.id} } + +func TestBroadcastAllAOI_GetNearby(t *testing.T) { + aoi := NewBroadcastAllAOI() + + e1 := &mockEntity{id: 1, pos: mathutil.NewVec3(0, 0, 0)} + e2 := &mockEntity{id: 2, pos: mathutil.NewVec3(100, 0, 100)} + e3 := &mockEntity{id: 3, pos: mathutil.NewVec3(999, 0, 999)} + + aoi.Add(e1) + aoi.Add(e2) + aoi.Add(e3) + + // With broadcast-all, everyone sees everyone. + nearby := aoi.GetNearby(e1) + if len(nearby) != 2 { + t.Errorf("expected 2 nearby, got %d", len(nearby)) + } +} + +func TestBroadcastAllAOI_Remove(t *testing.T) { + aoi := NewBroadcastAllAOI() + + e1 := &mockEntity{id: 1} + e2 := &mockEntity{id: 2} + aoi.Add(e1) + aoi.Add(e2) + + events := aoi.Remove(e1) + if len(events) != 1 { + t.Errorf("expected 1 leave event, got %d", len(events)) + } + if events[0].Type != AOILeave { + t.Errorf("expected AOILeave event") + } + + nearby := aoi.GetNearby(e2) + if len(nearby) != 0 { + t.Errorf("expected 0 nearby after removal, got %d", len(nearby)) + } +} + +func TestGridAOI_NearbyInSameCell(t *testing.T) { + aoi := NewGridAOI(50, 2) + + e1 := &mockEntity{id: 1, pos: mathutil.NewVec3(10, 0, 10)} + e2 := &mockEntity{id: 2, pos: mathutil.NewVec3(20, 0, 20)} + + aoi.Add(e1) + aoi.Add(e2) + + nearby := aoi.GetNearby(e1) + if len(nearby) != 1 { + t.Errorf("expected 1 nearby, got %d", len(nearby)) + } + if nearby[0].EntityID() != 2 { + t.Errorf("expected entity 2, got %d", nearby[0].EntityID()) + } +} + +func TestGridAOI_FarAwayNotVisible(t *testing.T) { + aoi := NewGridAOI(50, 1) // viewRange=1 means 3x3 grid = 150 units visibility + + e1 := &mockEntity{id: 1, pos: mathutil.NewVec3(0, 0, 0)} + e2 := &mockEntity{id: 2, pos: mathutil.NewVec3(500, 0, 500)} // far away + + aoi.Add(e1) + aoi.Add(e2) + + nearby := aoi.GetNearby(e1) + if len(nearby) != 0 { + t.Errorf("expected 0 nearby for far entity, got %d", len(nearby)) + } +} + +func TestGridAOI_MoveGeneratesEvents(t *testing.T) { + aoi := NewGridAOI(50, 1) + + e1 := &mockEntity{id: 1, pos: mathutil.NewVec3(0, 0, 0)} + e2 := &mockEntity{id: 2, pos: mathutil.NewVec3(200, 0, 200)} + + aoi.Add(e1) + aoi.Add(e2) + + // Initially not visible to each other. + nearby := aoi.GetNearby(e1) + if len(nearby) != 0 { + t.Fatalf("expected not visible initially, got %d", len(nearby)) + } + + // Move e2 close to e1. + oldPos := e2.pos + e2.pos = mathutil.NewVec3(10, 0, 10) + events := aoi.UpdatePosition(e2, oldPos, e2.pos) + + // Should generate enter events (e1 sees e2, e2 sees e1). + enterCount := 0 + for _, evt := range events { + if evt.Type == AOIEnter { + enterCount++ + } + } + if enterCount != 2 { + t.Errorf("expected 2 enter events, got %d", enterCount) + } +} + +func TestGridAOI_ToggleComparison(t *testing.T) { + // Demonstrates the difference between BroadcastAll and Grid AOI. + e1 := &mockEntity{id: 1, pos: mathutil.NewVec3(0, 0, 0)} + e2 := &mockEntity{id: 2, pos: mathutil.NewVec3(500, 0, 500)} + + // BroadcastAll: both visible + broadcast := NewBroadcastAllAOI() + broadcast.Add(e1) + broadcast.Add(e2) + if len(broadcast.GetNearby(e1)) != 1 { + t.Error("broadcast-all should see all entities") + } + + // Grid: e2 not visible from e1 (too far) + grid := NewGridAOI(50, 1) + grid.Add(e1) + grid.Add(e2) + if len(grid.GetNearby(e1)) != 0 { + t.Error("grid AOI should NOT see distant entities") + } +} diff --git a/internal/world/spatial_grid.go b/internal/world/spatial_grid.go new file mode 100644 index 0000000..622806a --- /dev/null +++ b/internal/world/spatial_grid.go @@ -0,0 +1,179 @@ +package world + +import ( + "a301_game_server/internal/entity" + "a301_game_server/pkg/mathutil" +) + +// cellKey uniquely identifies a grid cell. +type cellKey struct { + cx, cz int +} + +// GridAOI implements AOI using a spatial grid. Entities in nearby cells are considered visible. +type GridAOI struct { + cellSize float32 + viewRange int + cells map[cellKey]map[uint64]entity.Entity + entityCell map[uint64]cellKey +} + +func NewGridAOI(cellSize float32, viewRange int) *GridAOI { + return &GridAOI{ + cellSize: cellSize, + viewRange: viewRange, + cells: make(map[cellKey]map[uint64]entity.Entity), + entityCell: make(map[uint64]cellKey), + } +} + +func (g *GridAOI) posToCell(pos mathutil.Vec3) cellKey { + cx := int(pos.X / g.cellSize) + cz := int(pos.Z / g.cellSize) + if pos.X < 0 { + cx-- + } + if pos.Z < 0 { + cz-- + } + return cellKey{cx, cz} +} + +func (g *GridAOI) Add(ent entity.Entity) { + cell := g.posToCell(ent.Position()) + g.addToCell(cell, ent) + g.entityCell[ent.EntityID()] = cell +} + +func (g *GridAOI) Remove(ent entity.Entity) []AOIEvent { + eid := ent.EntityID() + cell, ok := g.entityCell[eid] + if !ok { + return nil + } + + nearby := g.getNearbyFromCell(cell, eid) + events := make([]AOIEvent, 0, len(nearby)) + for _, observer := range nearby { + events = append(events, AOIEvent{Observer: observer, Target: ent, Type: AOILeave}) + } + + g.removeFromCell(cell, eid) + delete(g.entityCell, eid) + return events +} + +func (g *GridAOI) UpdatePosition(ent entity.Entity, oldPos, newPos mathutil.Vec3) []AOIEvent { + eid := ent.EntityID() + oldCell := g.posToCell(oldPos) + newCell := g.posToCell(newPos) + + if oldCell == newCell { + return nil + } + + oldVisible := g.visibleCells(oldCell) + newVisible := g.visibleCells(newCell) + + leaving := cellDifference(oldVisible, newVisible) + entering := cellDifference(newVisible, oldVisible) + + var events []AOIEvent + + for _, c := range leaving { + if cellEntities, ok := g.cells[c]; ok { + for _, other := range cellEntities { + if other.EntityID() == eid { + continue + } + events = append(events, + AOIEvent{Observer: other, Target: ent, Type: AOILeave}, + AOIEvent{Observer: ent, Target: other, Type: AOILeave}, + ) + } + } + } + + for _, c := range entering { + if cellEntities, ok := g.cells[c]; ok { + for _, other := range cellEntities { + if other.EntityID() == eid { + continue + } + events = append(events, + AOIEvent{Observer: other, Target: ent, Type: AOIEnter}, + AOIEvent{Observer: ent, Target: other, Type: AOIEnter}, + ) + } + } + } + + g.removeFromCell(oldCell, eid) + g.addToCell(newCell, ent) + g.entityCell[eid] = newCell + + return events +} + +func (g *GridAOI) GetNearby(ent entity.Entity) []entity.Entity { + cell, ok := g.entityCell[ent.EntityID()] + if !ok { + return nil + } + return g.getNearbyFromCell(cell, ent.EntityID()) +} + +func (g *GridAOI) getNearbyFromCell(cell cellKey, excludeID uint64) []entity.Entity { + var result []entity.Entity + for _, c := range g.visibleCells(cell) { + if cellEntities, ok := g.cells[c]; ok { + for _, e := range cellEntities { + if e.EntityID() != excludeID { + result = append(result, e) + } + } + } + } + return result +} + +func (g *GridAOI) visibleCells(center cellKey) []cellKey { + size := (2*g.viewRange + 1) * (2*g.viewRange + 1) + cells := make([]cellKey, 0, size) + for dx := -g.viewRange; dx <= g.viewRange; dx++ { + for dz := -g.viewRange; dz <= g.viewRange; dz++ { + cells = append(cells, cellKey{center.cx + dx, center.cz + dz}) + } + } + return cells +} + +func (g *GridAOI) addToCell(cell cellKey, ent entity.Entity) { + if g.cells[cell] == nil { + g.cells[cell] = make(map[uint64]entity.Entity) + } + g.cells[cell][ent.EntityID()] = ent +} + +func (g *GridAOI) removeFromCell(cell cellKey, eid uint64) { + if m, ok := g.cells[cell]; ok { + delete(m, eid) + if len(m) == 0 { + delete(g.cells, cell) + } + } +} + +func cellDifference(a, b []cellKey) []cellKey { + set := make(map[cellKey]struct{}, len(b)) + for _, c := range b { + set[c] = struct{}{} + } + var diff []cellKey + for _, c := range a { + if _, ok := set[c]; !ok { + diff = append(diff, c) + } + } + return diff +} diff --git a/internal/world/zone_transfer.go b/internal/world/zone_transfer.go new file mode 100644 index 0000000..b059ff1 --- /dev/null +++ b/internal/world/zone_transfer.go @@ -0,0 +1,20 @@ +package world + +import "a301_game_server/pkg/mathutil" + +// ZonePortal defines a connection between two zones. +type ZonePortal struct { + // Trigger area in source zone. + SourceZoneID uint32 + TriggerPos mathutil.Vec3 + TriggerRadius float32 + + // Destination in target zone. + TargetZoneID uint32 + TargetPos mathutil.Vec3 +} + +// IsInRange returns true if the given position is within the portal's trigger area. +func (p *ZonePortal) IsInRange(pos mathutil.Vec3) bool { + return pos.DistanceXZ(p.TriggerPos) <= p.TriggerRadius +} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 0000000..2afbb69 --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,43 @@ +package logger + +import ( + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +var log *zap.SugaredLogger + +func Init(level string) error { + var zapLevel zapcore.Level + if err := zapLevel.UnmarshalText([]byte(level)); err != nil { + zapLevel = zapcore.InfoLevel + } + + cfg := zap.Config{ + Level: zap.NewAtomicLevelAt(zapLevel), + Encoding: "console", + EncoderConfig: zap.NewDevelopmentEncoderConfig(), + OutputPaths: []string{"stdout"}, + ErrorOutputPaths: []string{"stderr"}, + } + + l, err := cfg.Build(zap.AddCallerSkip(1)) + if err != nil { + return err + } + + log = l.Sugar() + return nil +} + +func Info(msg string, keysAndValues ...interface{}) { log.Infow(msg, keysAndValues...) } +func Warn(msg string, keysAndValues ...interface{}) { log.Warnw(msg, keysAndValues...) } +func Error(msg string, keysAndValues ...interface{}) { log.Errorw(msg, keysAndValues...) } +func Debug(msg string, keysAndValues ...interface{}) { log.Debugw(msg, keysAndValues...) } +func Fatal(msg string, keysAndValues ...interface{}) { log.Fatalw(msg, keysAndValues...) } + +func Sync() { + if log != nil { + _ = log.Sync() + } +} diff --git a/pkg/mathutil/vec3.go b/pkg/mathutil/vec3.go new file mode 100644 index 0000000..1d97c88 --- /dev/null +++ b/pkg/mathutil/vec3.go @@ -0,0 +1,56 @@ +package mathutil + +import "math" + +type Vec3 struct { + X float32 + Y float32 + Z float32 +} + +func NewVec3(x, y, z float32) Vec3 { + return Vec3{X: x, Y: y, Z: z} +} + +func (v Vec3) Add(other Vec3) Vec3 { + return Vec3{X: v.X + other.X, Y: v.Y + other.Y, Z: v.Z + other.Z} +} + +func (v Vec3) Sub(other Vec3) Vec3 { + return Vec3{X: v.X - other.X, Y: v.Y - other.Y, Z: v.Z - other.Z} +} + +func (v Vec3) Scale(s float32) Vec3 { + return Vec3{X: v.X * s, Y: v.Y * s, Z: v.Z * s} +} + +func (v Vec3) Length() float32 { + return float32(math.Sqrt(float64(v.X*v.X + v.Y*v.Y + v.Z*v.Z))) +} + +func (v Vec3) LengthSq() float32 { + return v.X*v.X + v.Y*v.Y + v.Z*v.Z +} + +func (v Vec3) Normalize() Vec3 { + l := v.Length() + if l < 1e-8 { + return Vec3{} + } + return v.Scale(1.0 / l) +} + +func (v Vec3) DistanceTo(other Vec3) float32 { + return v.Sub(other).Length() +} + +func (v Vec3) DistanceSqTo(other Vec3) float32 { + return v.Sub(other).LengthSq() +} + +// DistanceXZ returns distance ignoring Y axis (for ground-plane calculations). +func (v Vec3) DistanceXZ(other Vec3) float32 { + dx := v.X - other.X + dz := v.Z - other.Z + return float32(math.Sqrt(float64(dx*dx + dz*dz))) +} diff --git a/proto/gen/pb/messages.pb.go b/proto/gen/pb/messages.pb.go new file mode 100644 index 0000000..d1b03bb --- /dev/null +++ b/proto/gen/pb/messages.pb.go @@ -0,0 +1,1728 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v7.34.0 +// source: messages.proto + +package pb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type EntityType int32 + +const ( + EntityType_ENTITY_TYPE_PLAYER EntityType = 0 + EntityType_ENTITY_TYPE_MOB EntityType = 1 + EntityType_ENTITY_TYPE_NPC EntityType = 2 +) + +// Enum value maps for EntityType. +var ( + EntityType_name = map[int32]string{ + 0: "ENTITY_TYPE_PLAYER", + 1: "ENTITY_TYPE_MOB", + 2: "ENTITY_TYPE_NPC", + } + EntityType_value = map[string]int32{ + "ENTITY_TYPE_PLAYER": 0, + "ENTITY_TYPE_MOB": 1, + "ENTITY_TYPE_NPC": 2, + } +) + +func (x EntityType) Enum() *EntityType { + p := new(EntityType) + *p = x + return p +} + +func (x EntityType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (EntityType) Descriptor() protoreflect.EnumDescriptor { + return file_messages_proto_enumTypes[0].Descriptor() +} + +func (EntityType) Type() protoreflect.EnumType { + return &file_messages_proto_enumTypes[0] +} + +func (x EntityType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use EntityType.Descriptor instead. +func (EntityType) EnumDescriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{0} +} + +type CombatEventType int32 + +const ( + CombatEventType_COMBAT_EVENT_DAMAGE CombatEventType = 0 + CombatEventType_COMBAT_EVENT_HEAL CombatEventType = 1 + CombatEventType_COMBAT_EVENT_BUFF CombatEventType = 2 + CombatEventType_COMBAT_EVENT_DEBUFF CombatEventType = 3 + CombatEventType_COMBAT_EVENT_DEATH CombatEventType = 4 + CombatEventType_COMBAT_EVENT_RESPAWN CombatEventType = 5 +) + +// Enum value maps for CombatEventType. +var ( + CombatEventType_name = map[int32]string{ + 0: "COMBAT_EVENT_DAMAGE", + 1: "COMBAT_EVENT_HEAL", + 2: "COMBAT_EVENT_BUFF", + 3: "COMBAT_EVENT_DEBUFF", + 4: "COMBAT_EVENT_DEATH", + 5: "COMBAT_EVENT_RESPAWN", + } + CombatEventType_value = map[string]int32{ + "COMBAT_EVENT_DAMAGE": 0, + "COMBAT_EVENT_HEAL": 1, + "COMBAT_EVENT_BUFF": 2, + "COMBAT_EVENT_DEBUFF": 3, + "COMBAT_EVENT_DEATH": 4, + "COMBAT_EVENT_RESPAWN": 5, + } +) + +func (x CombatEventType) Enum() *CombatEventType { + p := new(CombatEventType) + *p = x + return p +} + +func (x CombatEventType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (CombatEventType) Descriptor() protoreflect.EnumDescriptor { + return file_messages_proto_enumTypes[1].Descriptor() +} + +func (CombatEventType) Type() protoreflect.EnumType { + return &file_messages_proto_enumTypes[1] +} + +func (x CombatEventType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use CombatEventType.Descriptor instead. +func (CombatEventType) EnumDescriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{1} +} + +type LoginRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LoginRequest) Reset() { + *x = LoginRequest{} + mi := &file_messages_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LoginRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LoginRequest) ProtoMessage() {} + +func (x *LoginRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead. +func (*LoginRequest) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{0} +} + +func (x *LoginRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *LoginRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type LoginResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + SessionToken string `protobuf:"bytes,2,opt,name=session_token,json=sessionToken,proto3" json:"session_token,omitempty"` + ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` + PlayerId uint64 `protobuf:"varint,4,opt,name=player_id,json=playerId,proto3" json:"player_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LoginResponse) Reset() { + *x = LoginResponse{} + mi := &file_messages_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LoginResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LoginResponse) ProtoMessage() {} + +func (x *LoginResponse) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LoginResponse.ProtoReflect.Descriptor instead. +func (*LoginResponse) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{1} +} + +func (x *LoginResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *LoginResponse) GetSessionToken() string { + if x != nil { + return x.SessionToken + } + return "" +} + +func (x *LoginResponse) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage + } + return "" +} + +func (x *LoginResponse) GetPlayerId() uint64 { + if x != nil { + return x.PlayerId + } + return 0 +} + +type EnterWorldRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + SessionToken string `protobuf:"bytes,1,opt,name=session_token,json=sessionToken,proto3" json:"session_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *EnterWorldRequest) Reset() { + *x = EnterWorldRequest{} + mi := &file_messages_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *EnterWorldRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EnterWorldRequest) ProtoMessage() {} + +func (x *EnterWorldRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EnterWorldRequest.ProtoReflect.Descriptor instead. +func (*EnterWorldRequest) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{2} +} + +func (x *EnterWorldRequest) GetSessionToken() string { + if x != nil { + return x.SessionToken + } + return "" +} + +type EnterWorldResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + ErrorMessage string `protobuf:"bytes,2,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` + Self *EntityState `protobuf:"bytes,3,opt,name=self,proto3" json:"self,omitempty"` + NearbyEntities []*EntityState `protobuf:"bytes,4,rep,name=nearby_entities,json=nearbyEntities,proto3" json:"nearby_entities,omitempty"` + ZoneId uint32 `protobuf:"varint,5,opt,name=zone_id,json=zoneId,proto3" json:"zone_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *EnterWorldResponse) Reset() { + *x = EnterWorldResponse{} + mi := &file_messages_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *EnterWorldResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EnterWorldResponse) ProtoMessage() {} + +func (x *EnterWorldResponse) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EnterWorldResponse.ProtoReflect.Descriptor instead. +func (*EnterWorldResponse) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{3} +} + +func (x *EnterWorldResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *EnterWorldResponse) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage + } + return "" +} + +func (x *EnterWorldResponse) GetSelf() *EntityState { + if x != nil { + return x.Self + } + return nil +} + +func (x *EnterWorldResponse) GetNearbyEntities() []*EntityState { + if x != nil { + return x.NearbyEntities + } + return nil +} + +func (x *EnterWorldResponse) GetZoneId() uint32 { + if x != nil { + return x.ZoneId + } + return 0 +} + +type Vector3 struct { + state protoimpl.MessageState `protogen:"open.v1"` + X float32 `protobuf:"fixed32,1,opt,name=x,proto3" json:"x,omitempty"` + Y float32 `protobuf:"fixed32,2,opt,name=y,proto3" json:"y,omitempty"` + Z float32 `protobuf:"fixed32,3,opt,name=z,proto3" json:"z,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Vector3) Reset() { + *x = Vector3{} + mi := &file_messages_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Vector3) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Vector3) ProtoMessage() {} + +func (x *Vector3) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Vector3.ProtoReflect.Descriptor instead. +func (*Vector3) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{4} +} + +func (x *Vector3) GetX() float32 { + if x != nil { + return x.X + } + return 0 +} + +func (x *Vector3) GetY() float32 { + if x != nil { + return x.Y + } + return 0 +} + +func (x *Vector3) GetZ() float32 { + if x != nil { + return x.Z + } + return 0 +} + +type EntityState struct { + state protoimpl.MessageState `protogen:"open.v1"` + EntityId uint64 `protobuf:"varint,1,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Position *Vector3 `protobuf:"bytes,3,opt,name=position,proto3" json:"position,omitempty"` + Rotation float32 `protobuf:"fixed32,4,opt,name=rotation,proto3" json:"rotation,omitempty"` + Hp int32 `protobuf:"varint,5,opt,name=hp,proto3" json:"hp,omitempty"` + MaxHp int32 `protobuf:"varint,6,opt,name=max_hp,json=maxHp,proto3" json:"max_hp,omitempty"` + Level int32 `protobuf:"varint,7,opt,name=level,proto3" json:"level,omitempty"` + EntityType EntityType `protobuf:"varint,8,opt,name=entity_type,json=entityType,proto3,enum=proto.EntityType" json:"entity_type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *EntityState) Reset() { + *x = EntityState{} + mi := &file_messages_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *EntityState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EntityState) ProtoMessage() {} + +func (x *EntityState) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EntityState.ProtoReflect.Descriptor instead. +func (*EntityState) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{5} +} + +func (x *EntityState) GetEntityId() uint64 { + if x != nil { + return x.EntityId + } + return 0 +} + +func (x *EntityState) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *EntityState) GetPosition() *Vector3 { + if x != nil { + return x.Position + } + return nil +} + +func (x *EntityState) GetRotation() float32 { + if x != nil { + return x.Rotation + } + return 0 +} + +func (x *EntityState) GetHp() int32 { + if x != nil { + return x.Hp + } + return 0 +} + +func (x *EntityState) GetMaxHp() int32 { + if x != nil { + return x.MaxHp + } + return 0 +} + +func (x *EntityState) GetLevel() int32 { + if x != nil { + return x.Level + } + return 0 +} + +func (x *EntityState) GetEntityType() EntityType { + if x != nil { + return x.EntityType + } + return EntityType_ENTITY_TYPE_PLAYER +} + +type MoveRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Position *Vector3 `protobuf:"bytes,1,opt,name=position,proto3" json:"position,omitempty"` + Rotation float32 `protobuf:"fixed32,2,opt,name=rotation,proto3" json:"rotation,omitempty"` + Velocity *Vector3 `protobuf:"bytes,3,opt,name=velocity,proto3" json:"velocity,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MoveRequest) Reset() { + *x = MoveRequest{} + mi := &file_messages_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MoveRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MoveRequest) ProtoMessage() {} + +func (x *MoveRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MoveRequest.ProtoReflect.Descriptor instead. +func (*MoveRequest) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{6} +} + +func (x *MoveRequest) GetPosition() *Vector3 { + if x != nil { + return x.Position + } + return nil +} + +func (x *MoveRequest) GetRotation() float32 { + if x != nil { + return x.Rotation + } + return 0 +} + +func (x *MoveRequest) GetVelocity() *Vector3 { + if x != nil { + return x.Velocity + } + return nil +} + +type StateUpdate struct { + state protoimpl.MessageState `protogen:"open.v1"` + Entities []*EntityState `protobuf:"bytes,1,rep,name=entities,proto3" json:"entities,omitempty"` + ServerTick int64 `protobuf:"varint,2,opt,name=server_tick,json=serverTick,proto3" json:"server_tick,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StateUpdate) Reset() { + *x = StateUpdate{} + mi := &file_messages_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StateUpdate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StateUpdate) ProtoMessage() {} + +func (x *StateUpdate) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StateUpdate.ProtoReflect.Descriptor instead. +func (*StateUpdate) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{7} +} + +func (x *StateUpdate) GetEntities() []*EntityState { + if x != nil { + return x.Entities + } + return nil +} + +func (x *StateUpdate) GetServerTick() int64 { + if x != nil { + return x.ServerTick + } + return 0 +} + +type SpawnEntity struct { + state protoimpl.MessageState `protogen:"open.v1"` + Entity *EntityState `protobuf:"bytes,1,opt,name=entity,proto3" json:"entity,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SpawnEntity) Reset() { + *x = SpawnEntity{} + mi := &file_messages_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SpawnEntity) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SpawnEntity) ProtoMessage() {} + +func (x *SpawnEntity) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SpawnEntity.ProtoReflect.Descriptor instead. +func (*SpawnEntity) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{8} +} + +func (x *SpawnEntity) GetEntity() *EntityState { + if x != nil { + return x.Entity + } + return nil +} + +type DespawnEntity struct { + state protoimpl.MessageState `protogen:"open.v1"` + EntityId uint64 `protobuf:"varint,1,opt,name=entity_id,json=entityId,proto3" json:"entity_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DespawnEntity) Reset() { + *x = DespawnEntity{} + mi := &file_messages_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DespawnEntity) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DespawnEntity) ProtoMessage() {} + +func (x *DespawnEntity) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DespawnEntity.ProtoReflect.Descriptor instead. +func (*DespawnEntity) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{9} +} + +func (x *DespawnEntity) GetEntityId() uint64 { + if x != nil { + return x.EntityId + } + return 0 +} + +type Ping struct { + state protoimpl.MessageState `protogen:"open.v1"` + ClientTime int64 `protobuf:"varint,1,opt,name=client_time,json=clientTime,proto3" json:"client_time,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Ping) Reset() { + *x = Ping{} + mi := &file_messages_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Ping) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Ping) ProtoMessage() {} + +func (x *Ping) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Ping.ProtoReflect.Descriptor instead. +func (*Ping) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{10} +} + +func (x *Ping) GetClientTime() int64 { + if x != nil { + return x.ClientTime + } + return 0 +} + +type Pong struct { + state protoimpl.MessageState `protogen:"open.v1"` + ClientTime int64 `protobuf:"varint,1,opt,name=client_time,json=clientTime,proto3" json:"client_time,omitempty"` + ServerTime int64 `protobuf:"varint,2,opt,name=server_time,json=serverTime,proto3" json:"server_time,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Pong) Reset() { + *x = Pong{} + mi := &file_messages_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Pong) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Pong) ProtoMessage() {} + +func (x *Pong) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Pong.ProtoReflect.Descriptor instead. +func (*Pong) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{11} +} + +func (x *Pong) GetClientTime() int64 { + if x != nil { + return x.ClientTime + } + return 0 +} + +func (x *Pong) GetServerTime() int64 { + if x != nil { + return x.ServerTime + } + return 0 +} + +type ZoneTransferNotify struct { + state protoimpl.MessageState `protogen:"open.v1"` + NewZoneId uint32 `protobuf:"varint,1,opt,name=new_zone_id,json=newZoneId,proto3" json:"new_zone_id,omitempty"` + Self *EntityState `protobuf:"bytes,2,opt,name=self,proto3" json:"self,omitempty"` + NearbyEntities []*EntityState `protobuf:"bytes,3,rep,name=nearby_entities,json=nearbyEntities,proto3" json:"nearby_entities,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ZoneTransferNotify) Reset() { + *x = ZoneTransferNotify{} + mi := &file_messages_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ZoneTransferNotify) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ZoneTransferNotify) ProtoMessage() {} + +func (x *ZoneTransferNotify) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ZoneTransferNotify.ProtoReflect.Descriptor instead. +func (*ZoneTransferNotify) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{12} +} + +func (x *ZoneTransferNotify) GetNewZoneId() uint32 { + if x != nil { + return x.NewZoneId + } + return 0 +} + +func (x *ZoneTransferNotify) GetSelf() *EntityState { + if x != nil { + return x.Self + } + return nil +} + +func (x *ZoneTransferNotify) GetNearbyEntities() []*EntityState { + if x != nil { + return x.NearbyEntities + } + return nil +} + +type UseSkillRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + SkillId uint32 `protobuf:"varint,1,opt,name=skill_id,json=skillId,proto3" json:"skill_id,omitempty"` + TargetId uint64 `protobuf:"varint,2,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty"` + TargetPos *Vector3 `protobuf:"bytes,3,opt,name=target_pos,json=targetPos,proto3" json:"target_pos,omitempty"` // for ground-targeted AoE + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UseSkillRequest) Reset() { + *x = UseSkillRequest{} + mi := &file_messages_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UseSkillRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UseSkillRequest) ProtoMessage() {} + +func (x *UseSkillRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UseSkillRequest.ProtoReflect.Descriptor instead. +func (*UseSkillRequest) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{13} +} + +func (x *UseSkillRequest) GetSkillId() uint32 { + if x != nil { + return x.SkillId + } + return 0 +} + +func (x *UseSkillRequest) GetTargetId() uint64 { + if x != nil { + return x.TargetId + } + return 0 +} + +func (x *UseSkillRequest) GetTargetPos() *Vector3 { + if x != nil { + return x.TargetPos + } + return nil +} + +type UseSkillResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + ErrorMessage string `protobuf:"bytes,2,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UseSkillResponse) Reset() { + *x = UseSkillResponse{} + mi := &file_messages_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UseSkillResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UseSkillResponse) ProtoMessage() {} + +func (x *UseSkillResponse) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UseSkillResponse.ProtoReflect.Descriptor instead. +func (*UseSkillResponse) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{14} +} + +func (x *UseSkillResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *UseSkillResponse) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage + } + return "" +} + +type CombatEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + CasterId uint64 `protobuf:"varint,1,opt,name=caster_id,json=casterId,proto3" json:"caster_id,omitempty"` + TargetId uint64 `protobuf:"varint,2,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty"` + SkillId uint32 `protobuf:"varint,3,opt,name=skill_id,json=skillId,proto3" json:"skill_id,omitempty"` + Damage int32 `protobuf:"varint,4,opt,name=damage,proto3" json:"damage,omitempty"` + Heal int32 `protobuf:"varint,5,opt,name=heal,proto3" json:"heal,omitempty"` + IsCritical bool `protobuf:"varint,6,opt,name=is_critical,json=isCritical,proto3" json:"is_critical,omitempty"` + TargetDied bool `protobuf:"varint,7,opt,name=target_died,json=targetDied,proto3" json:"target_died,omitempty"` + TargetHp int32 `protobuf:"varint,8,opt,name=target_hp,json=targetHp,proto3" json:"target_hp,omitempty"` + TargetMaxHp int32 `protobuf:"varint,9,opt,name=target_max_hp,json=targetMaxHp,proto3" json:"target_max_hp,omitempty"` + EventType CombatEventType `protobuf:"varint,10,opt,name=event_type,json=eventType,proto3,enum=proto.CombatEventType" json:"event_type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CombatEvent) Reset() { + *x = CombatEvent{} + mi := &file_messages_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CombatEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CombatEvent) ProtoMessage() {} + +func (x *CombatEvent) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CombatEvent.ProtoReflect.Descriptor instead. +func (*CombatEvent) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{15} +} + +func (x *CombatEvent) GetCasterId() uint64 { + if x != nil { + return x.CasterId + } + return 0 +} + +func (x *CombatEvent) GetTargetId() uint64 { + if x != nil { + return x.TargetId + } + return 0 +} + +func (x *CombatEvent) GetSkillId() uint32 { + if x != nil { + return x.SkillId + } + return 0 +} + +func (x *CombatEvent) GetDamage() int32 { + if x != nil { + return x.Damage + } + return 0 +} + +func (x *CombatEvent) GetHeal() int32 { + if x != nil { + return x.Heal + } + return 0 +} + +func (x *CombatEvent) GetIsCritical() bool { + if x != nil { + return x.IsCritical + } + return false +} + +func (x *CombatEvent) GetTargetDied() bool { + if x != nil { + return x.TargetDied + } + return false +} + +func (x *CombatEvent) GetTargetHp() int32 { + if x != nil { + return x.TargetHp + } + return 0 +} + +func (x *CombatEvent) GetTargetMaxHp() int32 { + if x != nil { + return x.TargetMaxHp + } + return 0 +} + +func (x *CombatEvent) GetEventType() CombatEventType { + if x != nil { + return x.EventType + } + return CombatEventType_COMBAT_EVENT_DAMAGE +} + +type BuffApplied struct { + state protoimpl.MessageState `protogen:"open.v1"` + TargetId uint64 `protobuf:"varint,1,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty"` + BuffId uint32 `protobuf:"varint,2,opt,name=buff_id,json=buffId,proto3" json:"buff_id,omitempty"` + BuffName string `protobuf:"bytes,3,opt,name=buff_name,json=buffName,proto3" json:"buff_name,omitempty"` + Duration float32 `protobuf:"fixed32,4,opt,name=duration,proto3" json:"duration,omitempty"` + IsDebuff bool `protobuf:"varint,5,opt,name=is_debuff,json=isDebuff,proto3" json:"is_debuff,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BuffApplied) Reset() { + *x = BuffApplied{} + mi := &file_messages_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BuffApplied) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BuffApplied) ProtoMessage() {} + +func (x *BuffApplied) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BuffApplied.ProtoReflect.Descriptor instead. +func (*BuffApplied) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{16} +} + +func (x *BuffApplied) GetTargetId() uint64 { + if x != nil { + return x.TargetId + } + return 0 +} + +func (x *BuffApplied) GetBuffId() uint32 { + if x != nil { + return x.BuffId + } + return 0 +} + +func (x *BuffApplied) GetBuffName() string { + if x != nil { + return x.BuffName + } + return "" +} + +func (x *BuffApplied) GetDuration() float32 { + if x != nil { + return x.Duration + } + return 0 +} + +func (x *BuffApplied) GetIsDebuff() bool { + if x != nil { + return x.IsDebuff + } + return false +} + +type BuffRemoved struct { + state protoimpl.MessageState `protogen:"open.v1"` + TargetId uint64 `protobuf:"varint,1,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty"` + BuffId uint32 `protobuf:"varint,2,opt,name=buff_id,json=buffId,proto3" json:"buff_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BuffRemoved) Reset() { + *x = BuffRemoved{} + mi := &file_messages_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BuffRemoved) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BuffRemoved) ProtoMessage() {} + +func (x *BuffRemoved) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BuffRemoved.ProtoReflect.Descriptor instead. +func (*BuffRemoved) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{17} +} + +func (x *BuffRemoved) GetTargetId() uint64 { + if x != nil { + return x.TargetId + } + return 0 +} + +func (x *BuffRemoved) GetBuffId() uint32 { + if x != nil { + return x.BuffId + } + return 0 +} + +type RespawnRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RespawnRequest) Reset() { + *x = RespawnRequest{} + mi := &file_messages_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RespawnRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RespawnRequest) ProtoMessage() {} + +func (x *RespawnRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RespawnRequest.ProtoReflect.Descriptor instead. +func (*RespawnRequest) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{18} +} + +type RespawnResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Self *EntityState `protobuf:"bytes,1,opt,name=self,proto3" json:"self,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RespawnResponse) Reset() { + *x = RespawnResponse{} + mi := &file_messages_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RespawnResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RespawnResponse) ProtoMessage() {} + +func (x *RespawnResponse) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RespawnResponse.ProtoReflect.Descriptor instead. +func (*RespawnResponse) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{19} +} + +func (x *RespawnResponse) GetSelf() *EntityState { + if x != nil { + return x.Self + } + return nil +} + +type AOIToggleRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AOIToggleRequest) Reset() { + *x = AOIToggleRequest{} + mi := &file_messages_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AOIToggleRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AOIToggleRequest) ProtoMessage() {} + +func (x *AOIToggleRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AOIToggleRequest.ProtoReflect.Descriptor instead. +func (*AOIToggleRequest) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{20} +} + +func (x *AOIToggleRequest) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +type AOIToggleResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *AOIToggleResponse) Reset() { + *x = AOIToggleResponse{} + mi := &file_messages_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *AOIToggleResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AOIToggleResponse) ProtoMessage() {} + +func (x *AOIToggleResponse) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AOIToggleResponse.ProtoReflect.Descriptor instead. +func (*AOIToggleResponse) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{21} +} + +func (x *AOIToggleResponse) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *AOIToggleResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type ServerMetrics struct { + state protoimpl.MessageState `protogen:"open.v1"` + OnlinePlayers int32 `protobuf:"varint,1,opt,name=online_players,json=onlinePlayers,proto3" json:"online_players,omitempty"` + TotalEntities int32 `protobuf:"varint,2,opt,name=total_entities,json=totalEntities,proto3" json:"total_entities,omitempty"` + TickDurationUs int64 `protobuf:"varint,3,opt,name=tick_duration_us,json=tickDurationUs,proto3" json:"tick_duration_us,omitempty"` + AoiEnabled bool `protobuf:"varint,4,opt,name=aoi_enabled,json=aoiEnabled,proto3" json:"aoi_enabled,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ServerMetrics) Reset() { + *x = ServerMetrics{} + mi := &file_messages_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ServerMetrics) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ServerMetrics) ProtoMessage() {} + +func (x *ServerMetrics) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ServerMetrics.ProtoReflect.Descriptor instead. +func (*ServerMetrics) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{22} +} + +func (x *ServerMetrics) GetOnlinePlayers() int32 { + if x != nil { + return x.OnlinePlayers + } + return 0 +} + +func (x *ServerMetrics) GetTotalEntities() int32 { + if x != nil { + return x.TotalEntities + } + return 0 +} + +func (x *ServerMetrics) GetTickDurationUs() int64 { + if x != nil { + return x.TickDurationUs + } + return 0 +} + +func (x *ServerMetrics) GetAoiEnabled() bool { + if x != nil { + return x.AoiEnabled + } + return false +} + +type MetricsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MetricsRequest) Reset() { + *x = MetricsRequest{} + mi := &file_messages_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MetricsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MetricsRequest) ProtoMessage() {} + +func (x *MetricsRequest) ProtoReflect() protoreflect.Message { + mi := &file_messages_proto_msgTypes[23] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MetricsRequest.ProtoReflect.Descriptor instead. +func (*MetricsRequest) Descriptor() ([]byte, []int) { + return file_messages_proto_rawDescGZIP(), []int{23} +} + +var File_messages_proto protoreflect.FileDescriptor + +const file_messages_proto_rawDesc = "" + + "\n" + + "\x0emessages.proto\x12\x05proto\"F\n" + + "\fLoginRequest\x12\x1a\n" + + "\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" + + "\bpassword\x18\x02 \x01(\tR\bpassword\"\x90\x01\n" + + "\rLoginResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess\x12#\n" + + "\rsession_token\x18\x02 \x01(\tR\fsessionToken\x12#\n" + + "\rerror_message\x18\x03 \x01(\tR\ferrorMessage\x12\x1b\n" + + "\tplayer_id\x18\x04 \x01(\x04R\bplayerId\"8\n" + + "\x11EnterWorldRequest\x12#\n" + + "\rsession_token\x18\x01 \x01(\tR\fsessionToken\"\xd1\x01\n" + + "\x12EnterWorldResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess\x12#\n" + + "\rerror_message\x18\x02 \x01(\tR\ferrorMessage\x12&\n" + + "\x04self\x18\x03 \x01(\v2\x12.proto.EntityStateR\x04self\x12;\n" + + "\x0fnearby_entities\x18\x04 \x03(\v2\x12.proto.EntityStateR\x0enearbyEntities\x12\x17\n" + + "\azone_id\x18\x05 \x01(\rR\x06zoneId\"3\n" + + "\aVector3\x12\f\n" + + "\x01x\x18\x01 \x01(\x02R\x01x\x12\f\n" + + "\x01y\x18\x02 \x01(\x02R\x01y\x12\f\n" + + "\x01z\x18\x03 \x01(\x02R\x01z\"\xf7\x01\n" + + "\vEntityState\x12\x1b\n" + + "\tentity_id\x18\x01 \x01(\x04R\bentityId\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12*\n" + + "\bposition\x18\x03 \x01(\v2\x0e.proto.Vector3R\bposition\x12\x1a\n" + + "\brotation\x18\x04 \x01(\x02R\brotation\x12\x0e\n" + + "\x02hp\x18\x05 \x01(\x05R\x02hp\x12\x15\n" + + "\x06max_hp\x18\x06 \x01(\x05R\x05maxHp\x12\x14\n" + + "\x05level\x18\a \x01(\x05R\x05level\x122\n" + + "\ventity_type\x18\b \x01(\x0e2\x11.proto.EntityTypeR\n" + + "entityType\"\x81\x01\n" + + "\vMoveRequest\x12*\n" + + "\bposition\x18\x01 \x01(\v2\x0e.proto.Vector3R\bposition\x12\x1a\n" + + "\brotation\x18\x02 \x01(\x02R\brotation\x12*\n" + + "\bvelocity\x18\x03 \x01(\v2\x0e.proto.Vector3R\bvelocity\"^\n" + + "\vStateUpdate\x12.\n" + + "\bentities\x18\x01 \x03(\v2\x12.proto.EntityStateR\bentities\x12\x1f\n" + + "\vserver_tick\x18\x02 \x01(\x03R\n" + + "serverTick\"9\n" + + "\vSpawnEntity\x12*\n" + + "\x06entity\x18\x01 \x01(\v2\x12.proto.EntityStateR\x06entity\",\n" + + "\rDespawnEntity\x12\x1b\n" + + "\tentity_id\x18\x01 \x01(\x04R\bentityId\"'\n" + + "\x04Ping\x12\x1f\n" + + "\vclient_time\x18\x01 \x01(\x03R\n" + + "clientTime\"H\n" + + "\x04Pong\x12\x1f\n" + + "\vclient_time\x18\x01 \x01(\x03R\n" + + "clientTime\x12\x1f\n" + + "\vserver_time\x18\x02 \x01(\x03R\n" + + "serverTime\"\x99\x01\n" + + "\x12ZoneTransferNotify\x12\x1e\n" + + "\vnew_zone_id\x18\x01 \x01(\rR\tnewZoneId\x12&\n" + + "\x04self\x18\x02 \x01(\v2\x12.proto.EntityStateR\x04self\x12;\n" + + "\x0fnearby_entities\x18\x03 \x03(\v2\x12.proto.EntityStateR\x0enearbyEntities\"x\n" + + "\x0fUseSkillRequest\x12\x19\n" + + "\bskill_id\x18\x01 \x01(\rR\askillId\x12\x1b\n" + + "\ttarget_id\x18\x02 \x01(\x04R\btargetId\x12-\n" + + "\n" + + "target_pos\x18\x03 \x01(\v2\x0e.proto.Vector3R\ttargetPos\"Q\n" + + "\x10UseSkillResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess\x12#\n" + + "\rerror_message\x18\x02 \x01(\tR\ferrorMessage\"\xc8\x02\n" + + "\vCombatEvent\x12\x1b\n" + + "\tcaster_id\x18\x01 \x01(\x04R\bcasterId\x12\x1b\n" + + "\ttarget_id\x18\x02 \x01(\x04R\btargetId\x12\x19\n" + + "\bskill_id\x18\x03 \x01(\rR\askillId\x12\x16\n" + + "\x06damage\x18\x04 \x01(\x05R\x06damage\x12\x12\n" + + "\x04heal\x18\x05 \x01(\x05R\x04heal\x12\x1f\n" + + "\vis_critical\x18\x06 \x01(\bR\n" + + "isCritical\x12\x1f\n" + + "\vtarget_died\x18\a \x01(\bR\n" + + "targetDied\x12\x1b\n" + + "\ttarget_hp\x18\b \x01(\x05R\btargetHp\x12\"\n" + + "\rtarget_max_hp\x18\t \x01(\x05R\vtargetMaxHp\x125\n" + + "\n" + + "event_type\x18\n" + + " \x01(\x0e2\x16.proto.CombatEventTypeR\teventType\"\x99\x01\n" + + "\vBuffApplied\x12\x1b\n" + + "\ttarget_id\x18\x01 \x01(\x04R\btargetId\x12\x17\n" + + "\abuff_id\x18\x02 \x01(\rR\x06buffId\x12\x1b\n" + + "\tbuff_name\x18\x03 \x01(\tR\bbuffName\x12\x1a\n" + + "\bduration\x18\x04 \x01(\x02R\bduration\x12\x1b\n" + + "\tis_debuff\x18\x05 \x01(\bR\bisDebuff\"C\n" + + "\vBuffRemoved\x12\x1b\n" + + "\ttarget_id\x18\x01 \x01(\x04R\btargetId\x12\x17\n" + + "\abuff_id\x18\x02 \x01(\rR\x06buffId\"\x10\n" + + "\x0eRespawnRequest\"9\n" + + "\x0fRespawnResponse\x12&\n" + + "\x04self\x18\x01 \x01(\v2\x12.proto.EntityStateR\x04self\",\n" + + "\x10AOIToggleRequest\x12\x18\n" + + "\aenabled\x18\x01 \x01(\bR\aenabled\"G\n" + + "\x11AOIToggleResponse\x12\x18\n" + + "\aenabled\x18\x01 \x01(\bR\aenabled\x12\x18\n" + + "\amessage\x18\x02 \x01(\tR\amessage\"\xa8\x01\n" + + "\rServerMetrics\x12%\n" + + "\x0eonline_players\x18\x01 \x01(\x05R\ronlinePlayers\x12%\n" + + "\x0etotal_entities\x18\x02 \x01(\x05R\rtotalEntities\x12(\n" + + "\x10tick_duration_us\x18\x03 \x01(\x03R\x0etickDurationUs\x12\x1f\n" + + "\vaoi_enabled\x18\x04 \x01(\bR\n" + + "aoiEnabled\"\x10\n" + + "\x0eMetricsRequest*N\n" + + "\n" + + "EntityType\x12\x16\n" + + "\x12ENTITY_TYPE_PLAYER\x10\x00\x12\x13\n" + + "\x0fENTITY_TYPE_MOB\x10\x01\x12\x13\n" + + "\x0fENTITY_TYPE_NPC\x10\x02*\xa3\x01\n" + + "\x0fCombatEventType\x12\x17\n" + + "\x13COMBAT_EVENT_DAMAGE\x10\x00\x12\x15\n" + + "\x11COMBAT_EVENT_HEAL\x10\x01\x12\x15\n" + + "\x11COMBAT_EVENT_BUFF\x10\x02\x12\x17\n" + + "\x13COMBAT_EVENT_DEBUFF\x10\x03\x12\x16\n" + + "\x12COMBAT_EVENT_DEATH\x10\x04\x12\x18\n" + + "\x14COMBAT_EVENT_RESPAWN\x10\x05B\x1fZ\x1da301_game_server/proto/gen/pbb\x06proto3" + +var ( + file_messages_proto_rawDescOnce sync.Once + file_messages_proto_rawDescData []byte +) + +func file_messages_proto_rawDescGZIP() []byte { + file_messages_proto_rawDescOnce.Do(func() { + file_messages_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_messages_proto_rawDesc), len(file_messages_proto_rawDesc))) + }) + return file_messages_proto_rawDescData +} + +var file_messages_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 24) +var file_messages_proto_goTypes = []any{ + (EntityType)(0), // 0: proto.EntityType + (CombatEventType)(0), // 1: proto.CombatEventType + (*LoginRequest)(nil), // 2: proto.LoginRequest + (*LoginResponse)(nil), // 3: proto.LoginResponse + (*EnterWorldRequest)(nil), // 4: proto.EnterWorldRequest + (*EnterWorldResponse)(nil), // 5: proto.EnterWorldResponse + (*Vector3)(nil), // 6: proto.Vector3 + (*EntityState)(nil), // 7: proto.EntityState + (*MoveRequest)(nil), // 8: proto.MoveRequest + (*StateUpdate)(nil), // 9: proto.StateUpdate + (*SpawnEntity)(nil), // 10: proto.SpawnEntity + (*DespawnEntity)(nil), // 11: proto.DespawnEntity + (*Ping)(nil), // 12: proto.Ping + (*Pong)(nil), // 13: proto.Pong + (*ZoneTransferNotify)(nil), // 14: proto.ZoneTransferNotify + (*UseSkillRequest)(nil), // 15: proto.UseSkillRequest + (*UseSkillResponse)(nil), // 16: proto.UseSkillResponse + (*CombatEvent)(nil), // 17: proto.CombatEvent + (*BuffApplied)(nil), // 18: proto.BuffApplied + (*BuffRemoved)(nil), // 19: proto.BuffRemoved + (*RespawnRequest)(nil), // 20: proto.RespawnRequest + (*RespawnResponse)(nil), // 21: proto.RespawnResponse + (*AOIToggleRequest)(nil), // 22: proto.AOIToggleRequest + (*AOIToggleResponse)(nil), // 23: proto.AOIToggleResponse + (*ServerMetrics)(nil), // 24: proto.ServerMetrics + (*MetricsRequest)(nil), // 25: proto.MetricsRequest +} +var file_messages_proto_depIdxs = []int32{ + 7, // 0: proto.EnterWorldResponse.self:type_name -> proto.EntityState + 7, // 1: proto.EnterWorldResponse.nearby_entities:type_name -> proto.EntityState + 6, // 2: proto.EntityState.position:type_name -> proto.Vector3 + 0, // 3: proto.EntityState.entity_type:type_name -> proto.EntityType + 6, // 4: proto.MoveRequest.position:type_name -> proto.Vector3 + 6, // 5: proto.MoveRequest.velocity:type_name -> proto.Vector3 + 7, // 6: proto.StateUpdate.entities:type_name -> proto.EntityState + 7, // 7: proto.SpawnEntity.entity:type_name -> proto.EntityState + 7, // 8: proto.ZoneTransferNotify.self:type_name -> proto.EntityState + 7, // 9: proto.ZoneTransferNotify.nearby_entities:type_name -> proto.EntityState + 6, // 10: proto.UseSkillRequest.target_pos:type_name -> proto.Vector3 + 1, // 11: proto.CombatEvent.event_type:type_name -> proto.CombatEventType + 7, // 12: proto.RespawnResponse.self:type_name -> proto.EntityState + 13, // [13:13] is the sub-list for method output_type + 13, // [13:13] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name +} + +func init() { file_messages_proto_init() } +func file_messages_proto_init() { + if File_messages_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_messages_proto_rawDesc), len(file_messages_proto_rawDesc)), + NumEnums: 2, + NumMessages: 24, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_messages_proto_goTypes, + DependencyIndexes: file_messages_proto_depIdxs, + EnumInfos: file_messages_proto_enumTypes, + MessageInfos: file_messages_proto_msgTypes, + }.Build() + File_messages_proto = out.File + file_messages_proto_goTypes = nil + file_messages_proto_depIdxs = nil +} diff --git a/proto/messages.proto b/proto/messages.proto new file mode 100644 index 0000000..83ede73 --- /dev/null +++ b/proto/messages.proto @@ -0,0 +1,170 @@ +syntax = "proto3"; + +package proto; + +option go_package = "a301_game_server/proto/gen/pb"; + +// ─── Authentication ──────────────────────────────────────── + +message LoginRequest { + string username = 1; + string password = 2; +} + +message LoginResponse { + bool success = 1; + string session_token = 2; + string error_message = 3; + uint64 player_id = 4; +} + +message EnterWorldRequest { + string session_token = 1; +} + +message EnterWorldResponse { + bool success = 1; + string error_message = 2; + EntityState self = 3; + repeated EntityState nearby_entities = 4; + uint32 zone_id = 5; +} + +// ─── Entity State ────────────────────────────────────────── + +message Vector3 { + float x = 1; + float y = 2; + float z = 3; +} + +message EntityState { + uint64 entity_id = 1; + string name = 2; + Vector3 position = 3; + float rotation = 4; + int32 hp = 5; + int32 max_hp = 6; + int32 level = 7; + EntityType entity_type = 8; +} + +enum EntityType { + ENTITY_TYPE_PLAYER = 0; + ENTITY_TYPE_MOB = 1; + ENTITY_TYPE_NPC = 2; +} + +// ─── Movement ────────────────────────────────────────────── + +message MoveRequest { + Vector3 position = 1; + float rotation = 2; + Vector3 velocity = 3; +} + +message StateUpdate { + repeated EntityState entities = 1; + int64 server_tick = 2; +} + +message SpawnEntity { + EntityState entity = 1; +} + +message DespawnEntity { + uint64 entity_id = 1; +} + +// ─── System ──────────────────────────────────────────────── + +message Ping { + int64 client_time = 1; +} + +message Pong { + int64 client_time = 1; + int64 server_time = 2; +} + +// ─── Zone Transfer ───────────────────────────────────────── + +message ZoneTransferNotify { + uint32 new_zone_id = 1; + EntityState self = 2; + repeated EntityState nearby_entities = 3; +} + +// ─── Combat ──────────────────────────────────────────────── + +message UseSkillRequest { + uint32 skill_id = 1; + uint64 target_id = 2; + Vector3 target_pos = 3; // for ground-targeted AoE +} + +message UseSkillResponse { + bool success = 1; + string error_message = 2; +} + +message CombatEvent { + uint64 caster_id = 1; + uint64 target_id = 2; + uint32 skill_id = 3; + int32 damage = 4; + int32 heal = 5; + bool is_critical = 6; + bool target_died = 7; + int32 target_hp = 8; + int32 target_max_hp = 9; + CombatEventType event_type = 10; +} + +enum CombatEventType { + COMBAT_EVENT_DAMAGE = 0; + COMBAT_EVENT_HEAL = 1; + COMBAT_EVENT_BUFF = 2; + COMBAT_EVENT_DEBUFF = 3; + COMBAT_EVENT_DEATH = 4; + COMBAT_EVENT_RESPAWN = 5; +} + +message BuffApplied { + uint64 target_id = 1; + uint32 buff_id = 2; + string buff_name = 3; + float duration = 4; + bool is_debuff = 5; +} + +message BuffRemoved { + uint64 target_id = 1; + uint32 buff_id = 2; +} + +message RespawnRequest {} + +message RespawnResponse { + EntityState self = 1; +} + +// ─── Admin / Debug ───────────────────────────────────────── + +message AOIToggleRequest { + bool enabled = 1; +} + +message AOIToggleResponse { + bool enabled = 1; + string message = 2; +} + +message ServerMetrics { + int32 online_players = 1; + int32 total_entities = 2; + int64 tick_duration_us = 3; + bool aoi_enabled = 4; +} + +message MetricsRequest {} diff --git a/testclient.exe b/testclient.exe new file mode 100644 index 0000000..11860db Binary files /dev/null and b/testclient.exe differ diff --git a/unity/Assets/Scripts/Proto/Messages.cs b/unity/Assets/Scripts/Proto/Messages.cs new file mode 100644 index 0000000..63e78df --- /dev/null +++ b/unity/Assets/Scripts/Proto/Messages.cs @@ -0,0 +1,6479 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: messages.proto +// +#pragma warning disable 1591, 0612, 3021, 8981 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace Proto { + + /// Holder for reflection information generated from messages.proto + public static partial class MessagesReflection { + + #region Descriptor + /// File descriptor for messages.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static MessagesReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "Cg5tZXNzYWdlcy5wcm90bxIFcHJvdG8iMgoMTG9naW5SZXF1ZXN0EhAKCHVz", + "ZXJuYW1lGAEgASgJEhAKCHBhc3N3b3JkGAIgASgJImEKDUxvZ2luUmVzcG9u", + "c2USDwoHc3VjY2VzcxgBIAEoCBIVCg1zZXNzaW9uX3Rva2VuGAIgASgJEhUK", + "DWVycm9yX21lc3NhZ2UYAyABKAkSEQoJcGxheWVyX2lkGAQgASgEIioKEUVu", + "dGVyV29ybGRSZXF1ZXN0EhUKDXNlc3Npb25fdG9rZW4YASABKAkinAEKEkVu", + "dGVyV29ybGRSZXNwb25zZRIPCgdzdWNjZXNzGAEgASgIEhUKDWVycm9yX21l", + "c3NhZ2UYAiABKAkSIAoEc2VsZhgDIAEoCzISLnByb3RvLkVudGl0eVN0YXRl", + "EisKD25lYXJieV9lbnRpdGllcxgEIAMoCzISLnByb3RvLkVudGl0eVN0YXRl", + "Eg8KB3pvbmVfaWQYBSABKA0iKgoHVmVjdG9yMxIJCgF4GAEgASgCEgkKAXkY", + "AiABKAISCQoBehgDIAEoAiK1AQoLRW50aXR5U3RhdGUSEQoJZW50aXR5X2lk", + "GAEgASgEEgwKBG5hbWUYAiABKAkSIAoIcG9zaXRpb24YAyABKAsyDi5wcm90", + "by5WZWN0b3IzEhAKCHJvdGF0aW9uGAQgASgCEgoKAmhwGAUgASgFEg4KBm1h", + "eF9ocBgGIAEoBRINCgVsZXZlbBgHIAEoBRImCgtlbnRpdHlfdHlwZRgIIAEo", + "DjIRLnByb3RvLkVudGl0eVR5cGUiYwoLTW92ZVJlcXVlc3QSIAoIcG9zaXRp", + "b24YASABKAsyDi5wcm90by5WZWN0b3IzEhAKCHJvdGF0aW9uGAIgASgCEiAK", + "CHZlbG9jaXR5GAMgASgLMg4ucHJvdG8uVmVjdG9yMyJICgtTdGF0ZVVwZGF0", + "ZRIkCghlbnRpdGllcxgBIAMoCzISLnByb3RvLkVudGl0eVN0YXRlEhMKC3Nl", + "cnZlcl90aWNrGAIgASgDIjEKC1NwYXduRW50aXR5EiIKBmVudGl0eRgBIAEo", + "CzISLnByb3RvLkVudGl0eVN0YXRlIiIKDURlc3Bhd25FbnRpdHkSEQoJZW50", + "aXR5X2lkGAEgASgEIhsKBFBpbmcSEwoLY2xpZW50X3RpbWUYASABKAMiMAoE", + "UG9uZxITCgtjbGllbnRfdGltZRgBIAEoAxITCgtzZXJ2ZXJfdGltZRgCIAEo", + "AyJ4ChJab25lVHJhbnNmZXJOb3RpZnkSEwoLbmV3X3pvbmVfaWQYASABKA0S", + "IAoEc2VsZhgCIAEoCzISLnByb3RvLkVudGl0eVN0YXRlEisKD25lYXJieV9l", + "bnRpdGllcxgDIAMoCzISLnByb3RvLkVudGl0eVN0YXRlIloKD1VzZVNraWxs", + "UmVxdWVzdBIQCghza2lsbF9pZBgBIAEoDRIRCgl0YXJnZXRfaWQYAiABKAQS", + "IgoKdGFyZ2V0X3BvcxgDIAEoCzIOLnByb3RvLlZlY3RvcjMiOgoQVXNlU2tp", + "bGxSZXNwb25zZRIPCgdzdWNjZXNzGAEgASgIEhUKDWVycm9yX21lc3NhZ2UY", + "AiABKAki4wEKC0NvbWJhdEV2ZW50EhEKCWNhc3Rlcl9pZBgBIAEoBBIRCgl0", + "YXJnZXRfaWQYAiABKAQSEAoIc2tpbGxfaWQYAyABKA0SDgoGZGFtYWdlGAQg", + "ASgFEgwKBGhlYWwYBSABKAUSEwoLaXNfY3JpdGljYWwYBiABKAgSEwoLdGFy", + "Z2V0X2RpZWQYByABKAgSEQoJdGFyZ2V0X2hwGAggASgFEhUKDXRhcmdldF9t", + "YXhfaHAYCSABKAUSKgoKZXZlbnRfdHlwZRgKIAEoDjIWLnByb3RvLkNvbWJh", + "dEV2ZW50VHlwZSJpCgtCdWZmQXBwbGllZBIRCgl0YXJnZXRfaWQYASABKAQS", + "DwoHYnVmZl9pZBgCIAEoDRIRCglidWZmX25hbWUYAyABKAkSEAoIZHVyYXRp", + "b24YBCABKAISEQoJaXNfZGVidWZmGAUgASgIIjEKC0J1ZmZSZW1vdmVkEhEK", + "CXRhcmdldF9pZBgBIAEoBBIPCgdidWZmX2lkGAIgASgNIhAKDlJlc3Bhd25S", + "ZXF1ZXN0IjMKD1Jlc3Bhd25SZXNwb25zZRIgCgRzZWxmGAEgASgLMhIucHJv", + "dG8uRW50aXR5U3RhdGUiIwoQQU9JVG9nZ2xlUmVxdWVzdBIPCgdlbmFibGVk", + "GAEgASgIIjUKEUFPSVRvZ2dsZVJlc3BvbnNlEg8KB2VuYWJsZWQYASABKAgS", + "DwoHbWVzc2FnZRgCIAEoCSJuCg1TZXJ2ZXJNZXRyaWNzEhYKDm9ubGluZV9w", + "bGF5ZXJzGAEgASgFEhYKDnRvdGFsX2VudGl0aWVzGAIgASgFEhgKEHRpY2tf", + "ZHVyYXRpb25fdXMYAyABKAMSEwoLYW9pX2VuYWJsZWQYBCABKAgiEAoOTWV0", + "cmljc1JlcXVlc3QqTgoKRW50aXR5VHlwZRIWChJFTlRJVFlfVFlQRV9QTEFZ", + "RVIQABITCg9FTlRJVFlfVFlQRV9NT0IQARITCg9FTlRJVFlfVFlQRV9OUEMQ", + "AiqjAQoPQ29tYmF0RXZlbnRUeXBlEhcKE0NPTUJBVF9FVkVOVF9EQU1BR0UQ", + "ABIVChFDT01CQVRfRVZFTlRfSEVBTBABEhUKEUNPTUJBVF9FVkVOVF9CVUZG", + "EAISFwoTQ09NQkFUX0VWRU5UX0RFQlVGRhADEhYKEkNPTUJBVF9FVkVOVF9E", + "RUFUSBAEEhgKFENPTUJBVF9FVkVOVF9SRVNQQVdOEAVCH1odYTMwMV9nYW1l", + "X3NlcnZlci9wcm90by9nZW4vcGJiBnByb3RvMw==")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Proto.EntityType), typeof(global::Proto.CombatEventType), }, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.LoginRequest), global::Proto.LoginRequest.Parser, new[]{ "Username", "Password" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.LoginResponse), global::Proto.LoginResponse.Parser, new[]{ "Success", "SessionToken", "ErrorMessage", "PlayerId" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.EnterWorldRequest), global::Proto.EnterWorldRequest.Parser, new[]{ "SessionToken" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.EnterWorldResponse), global::Proto.EnterWorldResponse.Parser, new[]{ "Success", "ErrorMessage", "Self", "NearbyEntities", "ZoneId" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.Vector3), global::Proto.Vector3.Parser, new[]{ "X", "Y", "Z" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.EntityState), global::Proto.EntityState.Parser, new[]{ "EntityId", "Name", "Position", "Rotation", "Hp", "MaxHp", "Level", "EntityType" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.MoveRequest), global::Proto.MoveRequest.Parser, new[]{ "Position", "Rotation", "Velocity" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.StateUpdate), global::Proto.StateUpdate.Parser, new[]{ "Entities", "ServerTick" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.SpawnEntity), global::Proto.SpawnEntity.Parser, new[]{ "Entity" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.DespawnEntity), global::Proto.DespawnEntity.Parser, new[]{ "EntityId" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.Ping), global::Proto.Ping.Parser, new[]{ "ClientTime" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.Pong), global::Proto.Pong.Parser, new[]{ "ClientTime", "ServerTime" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.ZoneTransferNotify), global::Proto.ZoneTransferNotify.Parser, new[]{ "NewZoneId", "Self", "NearbyEntities" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.UseSkillRequest), global::Proto.UseSkillRequest.Parser, new[]{ "SkillId", "TargetId", "TargetPos" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.UseSkillResponse), global::Proto.UseSkillResponse.Parser, new[]{ "Success", "ErrorMessage" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.CombatEvent), global::Proto.CombatEvent.Parser, new[]{ "CasterId", "TargetId", "SkillId", "Damage", "Heal", "IsCritical", "TargetDied", "TargetHp", "TargetMaxHp", "EventType" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.BuffApplied), global::Proto.BuffApplied.Parser, new[]{ "TargetId", "BuffId", "BuffName", "Duration", "IsDebuff" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.BuffRemoved), global::Proto.BuffRemoved.Parser, new[]{ "TargetId", "BuffId" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.RespawnRequest), global::Proto.RespawnRequest.Parser, null, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.RespawnResponse), global::Proto.RespawnResponse.Parser, new[]{ "Self" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.AOIToggleRequest), global::Proto.AOIToggleRequest.Parser, new[]{ "Enabled" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.AOIToggleResponse), global::Proto.AOIToggleResponse.Parser, new[]{ "Enabled", "Message" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.ServerMetrics), global::Proto.ServerMetrics.Parser, new[]{ "OnlinePlayers", "TotalEntities", "TickDurationUs", "AoiEnabled" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::Proto.MetricsRequest), global::Proto.MetricsRequest.Parser, null, null, null, null, null) + })); + } + #endregion + + } + #region Enums + public enum EntityType { + [pbr::OriginalName("ENTITY_TYPE_PLAYER")] Player = 0, + [pbr::OriginalName("ENTITY_TYPE_MOB")] Mob = 1, + [pbr::OriginalName("ENTITY_TYPE_NPC")] Npc = 2, + } + + public enum CombatEventType { + [pbr::OriginalName("COMBAT_EVENT_DAMAGE")] CombatEventDamage = 0, + [pbr::OriginalName("COMBAT_EVENT_HEAL")] CombatEventHeal = 1, + [pbr::OriginalName("COMBAT_EVENT_BUFF")] CombatEventBuff = 2, + [pbr::OriginalName("COMBAT_EVENT_DEBUFF")] CombatEventDebuff = 3, + [pbr::OriginalName("COMBAT_EVENT_DEATH")] CombatEventDeath = 4, + [pbr::OriginalName("COMBAT_EVENT_RESPAWN")] CombatEventRespawn = 5, + } + + #endregion + + #region Messages + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class LoginRequest : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new LoginRequest()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public LoginRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public LoginRequest(LoginRequest other) : this() { + username_ = other.username_; + password_ = other.password_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public LoginRequest Clone() { + return new LoginRequest(this); + } + + /// Field number for the "username" field. + public const int UsernameFieldNumber = 1; + private string username_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string Username { + get { return username_; } + set { + username_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "password" field. + public const int PasswordFieldNumber = 2; + private string password_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string Password { + get { return password_; } + set { + password_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as LoginRequest); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(LoginRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Username != other.Username) return false; + if (Password != other.Password) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (Username.Length != 0) hash ^= Username.GetHashCode(); + if (Password.Length != 0) hash ^= Password.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (Username.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Username); + } + if (Password.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Password); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (Username.Length != 0) { + output.WriteRawTag(10); + output.WriteString(Username); + } + if (Password.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Password); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (Username.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Username); + } + if (Password.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Password); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(LoginRequest other) { + if (other == null) { + return; + } + if (other.Username.Length != 0) { + Username = other.Username; + } + if (other.Password.Length != 0) { + Password = other.Password; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + Username = input.ReadString(); + break; + } + case 18: { + Password = input.ReadString(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + Username = input.ReadString(); + break; + } + case 18: { + Password = input.ReadString(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class LoginResponse : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new LoginResponse()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[1]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public LoginResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public LoginResponse(LoginResponse other) : this() { + success_ = other.success_; + sessionToken_ = other.sessionToken_; + errorMessage_ = other.errorMessage_; + playerId_ = other.playerId_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public LoginResponse Clone() { + return new LoginResponse(this); + } + + /// Field number for the "success" field. + public const int SuccessFieldNumber = 1; + private bool success_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Success { + get { return success_; } + set { + success_ = value; + } + } + + /// Field number for the "session_token" field. + public const int SessionTokenFieldNumber = 2; + private string sessionToken_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string SessionToken { + get { return sessionToken_; } + set { + sessionToken_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "error_message" field. + public const int ErrorMessageFieldNumber = 3; + private string errorMessage_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string ErrorMessage { + get { return errorMessage_; } + set { + errorMessage_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "player_id" field. + public const int PlayerIdFieldNumber = 4; + private ulong playerId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ulong PlayerId { + get { return playerId_; } + set { + playerId_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as LoginResponse); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(LoginResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Success != other.Success) return false; + if (SessionToken != other.SessionToken) return false; + if (ErrorMessage != other.ErrorMessage) return false; + if (PlayerId != other.PlayerId) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (Success != false) hash ^= Success.GetHashCode(); + if (SessionToken.Length != 0) hash ^= SessionToken.GetHashCode(); + if (ErrorMessage.Length != 0) hash ^= ErrorMessage.GetHashCode(); + if (PlayerId != 0UL) hash ^= PlayerId.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (Success != false) { + output.WriteRawTag(8); + output.WriteBool(Success); + } + if (SessionToken.Length != 0) { + output.WriteRawTag(18); + output.WriteString(SessionToken); + } + if (ErrorMessage.Length != 0) { + output.WriteRawTag(26); + output.WriteString(ErrorMessage); + } + if (PlayerId != 0UL) { + output.WriteRawTag(32); + output.WriteUInt64(PlayerId); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (Success != false) { + output.WriteRawTag(8); + output.WriteBool(Success); + } + if (SessionToken.Length != 0) { + output.WriteRawTag(18); + output.WriteString(SessionToken); + } + if (ErrorMessage.Length != 0) { + output.WriteRawTag(26); + output.WriteString(ErrorMessage); + } + if (PlayerId != 0UL) { + output.WriteRawTag(32); + output.WriteUInt64(PlayerId); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (Success != false) { + size += 1 + 1; + } + if (SessionToken.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(SessionToken); + } + if (ErrorMessage.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(ErrorMessage); + } + if (PlayerId != 0UL) { + size += 1 + pb::CodedOutputStream.ComputeUInt64Size(PlayerId); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(LoginResponse other) { + if (other == null) { + return; + } + if (other.Success != false) { + Success = other.Success; + } + if (other.SessionToken.Length != 0) { + SessionToken = other.SessionToken; + } + if (other.ErrorMessage.Length != 0) { + ErrorMessage = other.ErrorMessage; + } + if (other.PlayerId != 0UL) { + PlayerId = other.PlayerId; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + Success = input.ReadBool(); + break; + } + case 18: { + SessionToken = input.ReadString(); + break; + } + case 26: { + ErrorMessage = input.ReadString(); + break; + } + case 32: { + PlayerId = input.ReadUInt64(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + Success = input.ReadBool(); + break; + } + case 18: { + SessionToken = input.ReadString(); + break; + } + case 26: { + ErrorMessage = input.ReadString(); + break; + } + case 32: { + PlayerId = input.ReadUInt64(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class EnterWorldRequest : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new EnterWorldRequest()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[2]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public EnterWorldRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public EnterWorldRequest(EnterWorldRequest other) : this() { + sessionToken_ = other.sessionToken_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public EnterWorldRequest Clone() { + return new EnterWorldRequest(this); + } + + /// Field number for the "session_token" field. + public const int SessionTokenFieldNumber = 1; + private string sessionToken_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string SessionToken { + get { return sessionToken_; } + set { + sessionToken_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as EnterWorldRequest); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(EnterWorldRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (SessionToken != other.SessionToken) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (SessionToken.Length != 0) hash ^= SessionToken.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (SessionToken.Length != 0) { + output.WriteRawTag(10); + output.WriteString(SessionToken); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (SessionToken.Length != 0) { + output.WriteRawTag(10); + output.WriteString(SessionToken); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (SessionToken.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(SessionToken); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(EnterWorldRequest other) { + if (other == null) { + return; + } + if (other.SessionToken.Length != 0) { + SessionToken = other.SessionToken; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + SessionToken = input.ReadString(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + SessionToken = input.ReadString(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class EnterWorldResponse : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new EnterWorldResponse()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[3]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public EnterWorldResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public EnterWorldResponse(EnterWorldResponse other) : this() { + success_ = other.success_; + errorMessage_ = other.errorMessage_; + self_ = other.self_ != null ? other.self_.Clone() : null; + nearbyEntities_ = other.nearbyEntities_.Clone(); + zoneId_ = other.zoneId_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public EnterWorldResponse Clone() { + return new EnterWorldResponse(this); + } + + /// Field number for the "success" field. + public const int SuccessFieldNumber = 1; + private bool success_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Success { + get { return success_; } + set { + success_ = value; + } + } + + /// Field number for the "error_message" field. + public const int ErrorMessageFieldNumber = 2; + private string errorMessage_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string ErrorMessage { + get { return errorMessage_; } + set { + errorMessage_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "self" field. + public const int SelfFieldNumber = 3; + private global::Proto.EntityState self_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Proto.EntityState Self { + get { return self_; } + set { + self_ = value; + } + } + + /// Field number for the "nearby_entities" field. + public const int NearbyEntitiesFieldNumber = 4; + private static readonly pb::FieldCodec _repeated_nearbyEntities_codec + = pb::FieldCodec.ForMessage(34, global::Proto.EntityState.Parser); + private readonly pbc::RepeatedField nearbyEntities_ = new pbc::RepeatedField(); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pbc::RepeatedField NearbyEntities { + get { return nearbyEntities_; } + } + + /// Field number for the "zone_id" field. + public const int ZoneIdFieldNumber = 5; + private uint zoneId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public uint ZoneId { + get { return zoneId_; } + set { + zoneId_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as EnterWorldResponse); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(EnterWorldResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Success != other.Success) return false; + if (ErrorMessage != other.ErrorMessage) return false; + if (!object.Equals(Self, other.Self)) return false; + if(!nearbyEntities_.Equals(other.nearbyEntities_)) return false; + if (ZoneId != other.ZoneId) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (Success != false) hash ^= Success.GetHashCode(); + if (ErrorMessage.Length != 0) hash ^= ErrorMessage.GetHashCode(); + if (self_ != null) hash ^= Self.GetHashCode(); + hash ^= nearbyEntities_.GetHashCode(); + if (ZoneId != 0) hash ^= ZoneId.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (Success != false) { + output.WriteRawTag(8); + output.WriteBool(Success); + } + if (ErrorMessage.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ErrorMessage); + } + if (self_ != null) { + output.WriteRawTag(26); + output.WriteMessage(Self); + } + nearbyEntities_.WriteTo(output, _repeated_nearbyEntities_codec); + if (ZoneId != 0) { + output.WriteRawTag(40); + output.WriteUInt32(ZoneId); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (Success != false) { + output.WriteRawTag(8); + output.WriteBool(Success); + } + if (ErrorMessage.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ErrorMessage); + } + if (self_ != null) { + output.WriteRawTag(26); + output.WriteMessage(Self); + } + nearbyEntities_.WriteTo(ref output, _repeated_nearbyEntities_codec); + if (ZoneId != 0) { + output.WriteRawTag(40); + output.WriteUInt32(ZoneId); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (Success != false) { + size += 1 + 1; + } + if (ErrorMessage.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(ErrorMessage); + } + if (self_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Self); + } + size += nearbyEntities_.CalculateSize(_repeated_nearbyEntities_codec); + if (ZoneId != 0) { + size += 1 + pb::CodedOutputStream.ComputeUInt32Size(ZoneId); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(EnterWorldResponse other) { + if (other == null) { + return; + } + if (other.Success != false) { + Success = other.Success; + } + if (other.ErrorMessage.Length != 0) { + ErrorMessage = other.ErrorMessage; + } + if (other.self_ != null) { + if (self_ == null) { + Self = new global::Proto.EntityState(); + } + Self.MergeFrom(other.Self); + } + nearbyEntities_.Add(other.nearbyEntities_); + if (other.ZoneId != 0) { + ZoneId = other.ZoneId; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + Success = input.ReadBool(); + break; + } + case 18: { + ErrorMessage = input.ReadString(); + break; + } + case 26: { + if (self_ == null) { + Self = new global::Proto.EntityState(); + } + input.ReadMessage(Self); + break; + } + case 34: { + nearbyEntities_.AddEntriesFrom(input, _repeated_nearbyEntities_codec); + break; + } + case 40: { + ZoneId = input.ReadUInt32(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + Success = input.ReadBool(); + break; + } + case 18: { + ErrorMessage = input.ReadString(); + break; + } + case 26: { + if (self_ == null) { + Self = new global::Proto.EntityState(); + } + input.ReadMessage(Self); + break; + } + case 34: { + nearbyEntities_.AddEntriesFrom(ref input, _repeated_nearbyEntities_codec); + break; + } + case 40: { + ZoneId = input.ReadUInt32(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class Vector3 : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Vector3()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[4]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Vector3() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Vector3(Vector3 other) : this() { + x_ = other.x_; + y_ = other.y_; + z_ = other.z_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Vector3 Clone() { + return new Vector3(this); + } + + /// Field number for the "x" field. + public const int XFieldNumber = 1; + private float x_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public float X { + get { return x_; } + set { + x_ = value; + } + } + + /// Field number for the "y" field. + public const int YFieldNumber = 2; + private float y_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public float Y { + get { return y_; } + set { + y_ = value; + } + } + + /// Field number for the "z" field. + public const int ZFieldNumber = 3; + private float z_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public float Z { + get { return z_; } + set { + z_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as Vector3); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(Vector3 other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(X, other.X)) return false; + if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(Y, other.Y)) return false; + if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(Z, other.Z)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (X != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(X); + if (Y != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(Y); + if (Z != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(Z); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (X != 0F) { + output.WriteRawTag(13); + output.WriteFloat(X); + } + if (Y != 0F) { + output.WriteRawTag(21); + output.WriteFloat(Y); + } + if (Z != 0F) { + output.WriteRawTag(29); + output.WriteFloat(Z); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (X != 0F) { + output.WriteRawTag(13); + output.WriteFloat(X); + } + if (Y != 0F) { + output.WriteRawTag(21); + output.WriteFloat(Y); + } + if (Z != 0F) { + output.WriteRawTag(29); + output.WriteFloat(Z); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (X != 0F) { + size += 1 + 4; + } + if (Y != 0F) { + size += 1 + 4; + } + if (Z != 0F) { + size += 1 + 4; + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(Vector3 other) { + if (other == null) { + return; + } + if (other.X != 0F) { + X = other.X; + } + if (other.Y != 0F) { + Y = other.Y; + } + if (other.Z != 0F) { + Z = other.Z; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 13: { + X = input.ReadFloat(); + break; + } + case 21: { + Y = input.ReadFloat(); + break; + } + case 29: { + Z = input.ReadFloat(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 13: { + X = input.ReadFloat(); + break; + } + case 21: { + Y = input.ReadFloat(); + break; + } + case 29: { + Z = input.ReadFloat(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class EntityState : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new EntityState()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[5]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public EntityState() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public EntityState(EntityState other) : this() { + entityId_ = other.entityId_; + name_ = other.name_; + position_ = other.position_ != null ? other.position_.Clone() : null; + rotation_ = other.rotation_; + hp_ = other.hp_; + maxHp_ = other.maxHp_; + level_ = other.level_; + entityType_ = other.entityType_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public EntityState Clone() { + return new EntityState(this); + } + + /// Field number for the "entity_id" field. + public const int EntityIdFieldNumber = 1; + private ulong entityId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ulong EntityId { + get { return entityId_; } + set { + entityId_ = value; + } + } + + /// Field number for the "name" field. + public const int NameFieldNumber = 2; + private string name_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string Name { + get { return name_; } + set { + name_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "position" field. + public const int PositionFieldNumber = 3; + private global::Proto.Vector3 position_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Proto.Vector3 Position { + get { return position_; } + set { + position_ = value; + } + } + + /// Field number for the "rotation" field. + public const int RotationFieldNumber = 4; + private float rotation_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public float Rotation { + get { return rotation_; } + set { + rotation_ = value; + } + } + + /// Field number for the "hp" field. + public const int HpFieldNumber = 5; + private int hp_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int Hp { + get { return hp_; } + set { + hp_ = value; + } + } + + /// Field number for the "max_hp" field. + public const int MaxHpFieldNumber = 6; + private int maxHp_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int MaxHp { + get { return maxHp_; } + set { + maxHp_ = value; + } + } + + /// Field number for the "level" field. + public const int LevelFieldNumber = 7; + private int level_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int Level { + get { return level_; } + set { + level_ = value; + } + } + + /// Field number for the "entity_type" field. + public const int EntityTypeFieldNumber = 8; + private global::Proto.EntityType entityType_ = global::Proto.EntityType.Player; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Proto.EntityType EntityType { + get { return entityType_; } + set { + entityType_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as EntityState); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(EntityState other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (EntityId != other.EntityId) return false; + if (Name != other.Name) return false; + if (!object.Equals(Position, other.Position)) return false; + if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(Rotation, other.Rotation)) return false; + if (Hp != other.Hp) return false; + if (MaxHp != other.MaxHp) return false; + if (Level != other.Level) return false; + if (EntityType != other.EntityType) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (EntityId != 0UL) hash ^= EntityId.GetHashCode(); + if (Name.Length != 0) hash ^= Name.GetHashCode(); + if (position_ != null) hash ^= Position.GetHashCode(); + if (Rotation != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(Rotation); + if (Hp != 0) hash ^= Hp.GetHashCode(); + if (MaxHp != 0) hash ^= MaxHp.GetHashCode(); + if (Level != 0) hash ^= Level.GetHashCode(); + if (EntityType != global::Proto.EntityType.Player) hash ^= EntityType.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (EntityId != 0UL) { + output.WriteRawTag(8); + output.WriteUInt64(EntityId); + } + if (Name.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Name); + } + if (position_ != null) { + output.WriteRawTag(26); + output.WriteMessage(Position); + } + if (Rotation != 0F) { + output.WriteRawTag(37); + output.WriteFloat(Rotation); + } + if (Hp != 0) { + output.WriteRawTag(40); + output.WriteInt32(Hp); + } + if (MaxHp != 0) { + output.WriteRawTag(48); + output.WriteInt32(MaxHp); + } + if (Level != 0) { + output.WriteRawTag(56); + output.WriteInt32(Level); + } + if (EntityType != global::Proto.EntityType.Player) { + output.WriteRawTag(64); + output.WriteEnum((int) EntityType); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (EntityId != 0UL) { + output.WriteRawTag(8); + output.WriteUInt64(EntityId); + } + if (Name.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Name); + } + if (position_ != null) { + output.WriteRawTag(26); + output.WriteMessage(Position); + } + if (Rotation != 0F) { + output.WriteRawTag(37); + output.WriteFloat(Rotation); + } + if (Hp != 0) { + output.WriteRawTag(40); + output.WriteInt32(Hp); + } + if (MaxHp != 0) { + output.WriteRawTag(48); + output.WriteInt32(MaxHp); + } + if (Level != 0) { + output.WriteRawTag(56); + output.WriteInt32(Level); + } + if (EntityType != global::Proto.EntityType.Player) { + output.WriteRawTag(64); + output.WriteEnum((int) EntityType); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (EntityId != 0UL) { + size += 1 + pb::CodedOutputStream.ComputeUInt64Size(EntityId); + } + if (Name.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Name); + } + if (position_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Position); + } + if (Rotation != 0F) { + size += 1 + 4; + } + if (Hp != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Hp); + } + if (MaxHp != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(MaxHp); + } + if (Level != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Level); + } + if (EntityType != global::Proto.EntityType.Player) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) EntityType); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(EntityState other) { + if (other == null) { + return; + } + if (other.EntityId != 0UL) { + EntityId = other.EntityId; + } + if (other.Name.Length != 0) { + Name = other.Name; + } + if (other.position_ != null) { + if (position_ == null) { + Position = new global::Proto.Vector3(); + } + Position.MergeFrom(other.Position); + } + if (other.Rotation != 0F) { + Rotation = other.Rotation; + } + if (other.Hp != 0) { + Hp = other.Hp; + } + if (other.MaxHp != 0) { + MaxHp = other.MaxHp; + } + if (other.Level != 0) { + Level = other.Level; + } + if (other.EntityType != global::Proto.EntityType.Player) { + EntityType = other.EntityType; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + EntityId = input.ReadUInt64(); + break; + } + case 18: { + Name = input.ReadString(); + break; + } + case 26: { + if (position_ == null) { + Position = new global::Proto.Vector3(); + } + input.ReadMessage(Position); + break; + } + case 37: { + Rotation = input.ReadFloat(); + break; + } + case 40: { + Hp = input.ReadInt32(); + break; + } + case 48: { + MaxHp = input.ReadInt32(); + break; + } + case 56: { + Level = input.ReadInt32(); + break; + } + case 64: { + EntityType = (global::Proto.EntityType) input.ReadEnum(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + EntityId = input.ReadUInt64(); + break; + } + case 18: { + Name = input.ReadString(); + break; + } + case 26: { + if (position_ == null) { + Position = new global::Proto.Vector3(); + } + input.ReadMessage(Position); + break; + } + case 37: { + Rotation = input.ReadFloat(); + break; + } + case 40: { + Hp = input.ReadInt32(); + break; + } + case 48: { + MaxHp = input.ReadInt32(); + break; + } + case 56: { + Level = input.ReadInt32(); + break; + } + case 64: { + EntityType = (global::Proto.EntityType) input.ReadEnum(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class MoveRequest : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new MoveRequest()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[6]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public MoveRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public MoveRequest(MoveRequest other) : this() { + position_ = other.position_ != null ? other.position_.Clone() : null; + rotation_ = other.rotation_; + velocity_ = other.velocity_ != null ? other.velocity_.Clone() : null; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public MoveRequest Clone() { + return new MoveRequest(this); + } + + /// Field number for the "position" field. + public const int PositionFieldNumber = 1; + private global::Proto.Vector3 position_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Proto.Vector3 Position { + get { return position_; } + set { + position_ = value; + } + } + + /// Field number for the "rotation" field. + public const int RotationFieldNumber = 2; + private float rotation_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public float Rotation { + get { return rotation_; } + set { + rotation_ = value; + } + } + + /// Field number for the "velocity" field. + public const int VelocityFieldNumber = 3; + private global::Proto.Vector3 velocity_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Proto.Vector3 Velocity { + get { return velocity_; } + set { + velocity_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as MoveRequest); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(MoveRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (!object.Equals(Position, other.Position)) return false; + if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(Rotation, other.Rotation)) return false; + if (!object.Equals(Velocity, other.Velocity)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (position_ != null) hash ^= Position.GetHashCode(); + if (Rotation != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(Rotation); + if (velocity_ != null) hash ^= Velocity.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (position_ != null) { + output.WriteRawTag(10); + output.WriteMessage(Position); + } + if (Rotation != 0F) { + output.WriteRawTag(21); + output.WriteFloat(Rotation); + } + if (velocity_ != null) { + output.WriteRawTag(26); + output.WriteMessage(Velocity); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (position_ != null) { + output.WriteRawTag(10); + output.WriteMessage(Position); + } + if (Rotation != 0F) { + output.WriteRawTag(21); + output.WriteFloat(Rotation); + } + if (velocity_ != null) { + output.WriteRawTag(26); + output.WriteMessage(Velocity); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (position_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Position); + } + if (Rotation != 0F) { + size += 1 + 4; + } + if (velocity_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Velocity); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(MoveRequest other) { + if (other == null) { + return; + } + if (other.position_ != null) { + if (position_ == null) { + Position = new global::Proto.Vector3(); + } + Position.MergeFrom(other.Position); + } + if (other.Rotation != 0F) { + Rotation = other.Rotation; + } + if (other.velocity_ != null) { + if (velocity_ == null) { + Velocity = new global::Proto.Vector3(); + } + Velocity.MergeFrom(other.Velocity); + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + if (position_ == null) { + Position = new global::Proto.Vector3(); + } + input.ReadMessage(Position); + break; + } + case 21: { + Rotation = input.ReadFloat(); + break; + } + case 26: { + if (velocity_ == null) { + Velocity = new global::Proto.Vector3(); + } + input.ReadMessage(Velocity); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + if (position_ == null) { + Position = new global::Proto.Vector3(); + } + input.ReadMessage(Position); + break; + } + case 21: { + Rotation = input.ReadFloat(); + break; + } + case 26: { + if (velocity_ == null) { + Velocity = new global::Proto.Vector3(); + } + input.ReadMessage(Velocity); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class StateUpdate : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new StateUpdate()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[7]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public StateUpdate() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public StateUpdate(StateUpdate other) : this() { + entities_ = other.entities_.Clone(); + serverTick_ = other.serverTick_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public StateUpdate Clone() { + return new StateUpdate(this); + } + + /// Field number for the "entities" field. + public const int EntitiesFieldNumber = 1; + private static readonly pb::FieldCodec _repeated_entities_codec + = pb::FieldCodec.ForMessage(10, global::Proto.EntityState.Parser); + private readonly pbc::RepeatedField entities_ = new pbc::RepeatedField(); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pbc::RepeatedField Entities { + get { return entities_; } + } + + /// Field number for the "server_tick" field. + public const int ServerTickFieldNumber = 2; + private long serverTick_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public long ServerTick { + get { return serverTick_; } + set { + serverTick_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as StateUpdate); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(StateUpdate other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if(!entities_.Equals(other.entities_)) return false; + if (ServerTick != other.ServerTick) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + hash ^= entities_.GetHashCode(); + if (ServerTick != 0L) hash ^= ServerTick.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + entities_.WriteTo(output, _repeated_entities_codec); + if (ServerTick != 0L) { + output.WriteRawTag(16); + output.WriteInt64(ServerTick); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + entities_.WriteTo(ref output, _repeated_entities_codec); + if (ServerTick != 0L) { + output.WriteRawTag(16); + output.WriteInt64(ServerTick); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + size += entities_.CalculateSize(_repeated_entities_codec); + if (ServerTick != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(ServerTick); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(StateUpdate other) { + if (other == null) { + return; + } + entities_.Add(other.entities_); + if (other.ServerTick != 0L) { + ServerTick = other.ServerTick; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + entities_.AddEntriesFrom(input, _repeated_entities_codec); + break; + } + case 16: { + ServerTick = input.ReadInt64(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + entities_.AddEntriesFrom(ref input, _repeated_entities_codec); + break; + } + case 16: { + ServerTick = input.ReadInt64(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class SpawnEntity : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new SpawnEntity()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[8]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public SpawnEntity() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public SpawnEntity(SpawnEntity other) : this() { + entity_ = other.entity_ != null ? other.entity_.Clone() : null; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public SpawnEntity Clone() { + return new SpawnEntity(this); + } + + /// Field number for the "entity" field. + public const int EntityFieldNumber = 1; + private global::Proto.EntityState entity_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Proto.EntityState Entity { + get { return entity_; } + set { + entity_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as SpawnEntity); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(SpawnEntity other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (!object.Equals(Entity, other.Entity)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (entity_ != null) hash ^= Entity.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (entity_ != null) { + output.WriteRawTag(10); + output.WriteMessage(Entity); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (entity_ != null) { + output.WriteRawTag(10); + output.WriteMessage(Entity); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (entity_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Entity); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(SpawnEntity other) { + if (other == null) { + return; + } + if (other.entity_ != null) { + if (entity_ == null) { + Entity = new global::Proto.EntityState(); + } + Entity.MergeFrom(other.Entity); + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + if (entity_ == null) { + Entity = new global::Proto.EntityState(); + } + input.ReadMessage(Entity); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + if (entity_ == null) { + Entity = new global::Proto.EntityState(); + } + input.ReadMessage(Entity); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class DespawnEntity : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new DespawnEntity()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[9]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public DespawnEntity() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public DespawnEntity(DespawnEntity other) : this() { + entityId_ = other.entityId_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public DespawnEntity Clone() { + return new DespawnEntity(this); + } + + /// Field number for the "entity_id" field. + public const int EntityIdFieldNumber = 1; + private ulong entityId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ulong EntityId { + get { return entityId_; } + set { + entityId_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as DespawnEntity); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(DespawnEntity other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (EntityId != other.EntityId) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (EntityId != 0UL) hash ^= EntityId.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (EntityId != 0UL) { + output.WriteRawTag(8); + output.WriteUInt64(EntityId); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (EntityId != 0UL) { + output.WriteRawTag(8); + output.WriteUInt64(EntityId); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (EntityId != 0UL) { + size += 1 + pb::CodedOutputStream.ComputeUInt64Size(EntityId); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(DespawnEntity other) { + if (other == null) { + return; + } + if (other.EntityId != 0UL) { + EntityId = other.EntityId; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + EntityId = input.ReadUInt64(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + EntityId = input.ReadUInt64(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class Ping : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Ping()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[10]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Ping() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Ping(Ping other) : this() { + clientTime_ = other.clientTime_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Ping Clone() { + return new Ping(this); + } + + /// Field number for the "client_time" field. + public const int ClientTimeFieldNumber = 1; + private long clientTime_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public long ClientTime { + get { return clientTime_; } + set { + clientTime_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as Ping); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(Ping other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (ClientTime != other.ClientTime) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (ClientTime != 0L) hash ^= ClientTime.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (ClientTime != 0L) { + output.WriteRawTag(8); + output.WriteInt64(ClientTime); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (ClientTime != 0L) { + output.WriteRawTag(8); + output.WriteInt64(ClientTime); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (ClientTime != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(ClientTime); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(Ping other) { + if (other == null) { + return; + } + if (other.ClientTime != 0L) { + ClientTime = other.ClientTime; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + ClientTime = input.ReadInt64(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + ClientTime = input.ReadInt64(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class Pong : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Pong()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[11]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Pong() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Pong(Pong other) : this() { + clientTime_ = other.clientTime_; + serverTime_ = other.serverTime_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public Pong Clone() { + return new Pong(this); + } + + /// Field number for the "client_time" field. + public const int ClientTimeFieldNumber = 1; + private long clientTime_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public long ClientTime { + get { return clientTime_; } + set { + clientTime_ = value; + } + } + + /// Field number for the "server_time" field. + public const int ServerTimeFieldNumber = 2; + private long serverTime_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public long ServerTime { + get { return serverTime_; } + set { + serverTime_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as Pong); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(Pong other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (ClientTime != other.ClientTime) return false; + if (ServerTime != other.ServerTime) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (ClientTime != 0L) hash ^= ClientTime.GetHashCode(); + if (ServerTime != 0L) hash ^= ServerTime.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (ClientTime != 0L) { + output.WriteRawTag(8); + output.WriteInt64(ClientTime); + } + if (ServerTime != 0L) { + output.WriteRawTag(16); + output.WriteInt64(ServerTime); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (ClientTime != 0L) { + output.WriteRawTag(8); + output.WriteInt64(ClientTime); + } + if (ServerTime != 0L) { + output.WriteRawTag(16); + output.WriteInt64(ServerTime); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (ClientTime != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(ClientTime); + } + if (ServerTime != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(ServerTime); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(Pong other) { + if (other == null) { + return; + } + if (other.ClientTime != 0L) { + ClientTime = other.ClientTime; + } + if (other.ServerTime != 0L) { + ServerTime = other.ServerTime; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + ClientTime = input.ReadInt64(); + break; + } + case 16: { + ServerTime = input.ReadInt64(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + ClientTime = input.ReadInt64(); + break; + } + case 16: { + ServerTime = input.ReadInt64(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class ZoneTransferNotify : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new ZoneTransferNotify()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[12]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ZoneTransferNotify() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ZoneTransferNotify(ZoneTransferNotify other) : this() { + newZoneId_ = other.newZoneId_; + self_ = other.self_ != null ? other.self_.Clone() : null; + nearbyEntities_ = other.nearbyEntities_.Clone(); + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ZoneTransferNotify Clone() { + return new ZoneTransferNotify(this); + } + + /// Field number for the "new_zone_id" field. + public const int NewZoneIdFieldNumber = 1; + private uint newZoneId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public uint NewZoneId { + get { return newZoneId_; } + set { + newZoneId_ = value; + } + } + + /// Field number for the "self" field. + public const int SelfFieldNumber = 2; + private global::Proto.EntityState self_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Proto.EntityState Self { + get { return self_; } + set { + self_ = value; + } + } + + /// Field number for the "nearby_entities" field. + public const int NearbyEntitiesFieldNumber = 3; + private static readonly pb::FieldCodec _repeated_nearbyEntities_codec + = pb::FieldCodec.ForMessage(26, global::Proto.EntityState.Parser); + private readonly pbc::RepeatedField nearbyEntities_ = new pbc::RepeatedField(); + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public pbc::RepeatedField NearbyEntities { + get { return nearbyEntities_; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as ZoneTransferNotify); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(ZoneTransferNotify other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (NewZoneId != other.NewZoneId) return false; + if (!object.Equals(Self, other.Self)) return false; + if(!nearbyEntities_.Equals(other.nearbyEntities_)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (NewZoneId != 0) hash ^= NewZoneId.GetHashCode(); + if (self_ != null) hash ^= Self.GetHashCode(); + hash ^= nearbyEntities_.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (NewZoneId != 0) { + output.WriteRawTag(8); + output.WriteUInt32(NewZoneId); + } + if (self_ != null) { + output.WriteRawTag(18); + output.WriteMessage(Self); + } + nearbyEntities_.WriteTo(output, _repeated_nearbyEntities_codec); + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (NewZoneId != 0) { + output.WriteRawTag(8); + output.WriteUInt32(NewZoneId); + } + if (self_ != null) { + output.WriteRawTag(18); + output.WriteMessage(Self); + } + nearbyEntities_.WriteTo(ref output, _repeated_nearbyEntities_codec); + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (NewZoneId != 0) { + size += 1 + pb::CodedOutputStream.ComputeUInt32Size(NewZoneId); + } + if (self_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Self); + } + size += nearbyEntities_.CalculateSize(_repeated_nearbyEntities_codec); + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(ZoneTransferNotify other) { + if (other == null) { + return; + } + if (other.NewZoneId != 0) { + NewZoneId = other.NewZoneId; + } + if (other.self_ != null) { + if (self_ == null) { + Self = new global::Proto.EntityState(); + } + Self.MergeFrom(other.Self); + } + nearbyEntities_.Add(other.nearbyEntities_); + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + NewZoneId = input.ReadUInt32(); + break; + } + case 18: { + if (self_ == null) { + Self = new global::Proto.EntityState(); + } + input.ReadMessage(Self); + break; + } + case 26: { + nearbyEntities_.AddEntriesFrom(input, _repeated_nearbyEntities_codec); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + NewZoneId = input.ReadUInt32(); + break; + } + case 18: { + if (self_ == null) { + Self = new global::Proto.EntityState(); + } + input.ReadMessage(Self); + break; + } + case 26: { + nearbyEntities_.AddEntriesFrom(ref input, _repeated_nearbyEntities_codec); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class UseSkillRequest : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new UseSkillRequest()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[13]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public UseSkillRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public UseSkillRequest(UseSkillRequest other) : this() { + skillId_ = other.skillId_; + targetId_ = other.targetId_; + targetPos_ = other.targetPos_ != null ? other.targetPos_.Clone() : null; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public UseSkillRequest Clone() { + return new UseSkillRequest(this); + } + + /// Field number for the "skill_id" field. + public const int SkillIdFieldNumber = 1; + private uint skillId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public uint SkillId { + get { return skillId_; } + set { + skillId_ = value; + } + } + + /// Field number for the "target_id" field. + public const int TargetIdFieldNumber = 2; + private ulong targetId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ulong TargetId { + get { return targetId_; } + set { + targetId_ = value; + } + } + + /// Field number for the "target_pos" field. + public const int TargetPosFieldNumber = 3; + private global::Proto.Vector3 targetPos_; + /// + /// for ground-targeted AoE + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Proto.Vector3 TargetPos { + get { return targetPos_; } + set { + targetPos_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as UseSkillRequest); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(UseSkillRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (SkillId != other.SkillId) return false; + if (TargetId != other.TargetId) return false; + if (!object.Equals(TargetPos, other.TargetPos)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (SkillId != 0) hash ^= SkillId.GetHashCode(); + if (TargetId != 0UL) hash ^= TargetId.GetHashCode(); + if (targetPos_ != null) hash ^= TargetPos.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (SkillId != 0) { + output.WriteRawTag(8); + output.WriteUInt32(SkillId); + } + if (TargetId != 0UL) { + output.WriteRawTag(16); + output.WriteUInt64(TargetId); + } + if (targetPos_ != null) { + output.WriteRawTag(26); + output.WriteMessage(TargetPos); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (SkillId != 0) { + output.WriteRawTag(8); + output.WriteUInt32(SkillId); + } + if (TargetId != 0UL) { + output.WriteRawTag(16); + output.WriteUInt64(TargetId); + } + if (targetPos_ != null) { + output.WriteRawTag(26); + output.WriteMessage(TargetPos); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (SkillId != 0) { + size += 1 + pb::CodedOutputStream.ComputeUInt32Size(SkillId); + } + if (TargetId != 0UL) { + size += 1 + pb::CodedOutputStream.ComputeUInt64Size(TargetId); + } + if (targetPos_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(TargetPos); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(UseSkillRequest other) { + if (other == null) { + return; + } + if (other.SkillId != 0) { + SkillId = other.SkillId; + } + if (other.TargetId != 0UL) { + TargetId = other.TargetId; + } + if (other.targetPos_ != null) { + if (targetPos_ == null) { + TargetPos = new global::Proto.Vector3(); + } + TargetPos.MergeFrom(other.TargetPos); + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + SkillId = input.ReadUInt32(); + break; + } + case 16: { + TargetId = input.ReadUInt64(); + break; + } + case 26: { + if (targetPos_ == null) { + TargetPos = new global::Proto.Vector3(); + } + input.ReadMessage(TargetPos); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + SkillId = input.ReadUInt32(); + break; + } + case 16: { + TargetId = input.ReadUInt64(); + break; + } + case 26: { + if (targetPos_ == null) { + TargetPos = new global::Proto.Vector3(); + } + input.ReadMessage(TargetPos); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class UseSkillResponse : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new UseSkillResponse()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[14]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public UseSkillResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public UseSkillResponse(UseSkillResponse other) : this() { + success_ = other.success_; + errorMessage_ = other.errorMessage_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public UseSkillResponse Clone() { + return new UseSkillResponse(this); + } + + /// Field number for the "success" field. + public const int SuccessFieldNumber = 1; + private bool success_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Success { + get { return success_; } + set { + success_ = value; + } + } + + /// Field number for the "error_message" field. + public const int ErrorMessageFieldNumber = 2; + private string errorMessage_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string ErrorMessage { + get { return errorMessage_; } + set { + errorMessage_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as UseSkillResponse); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(UseSkillResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Success != other.Success) return false; + if (ErrorMessage != other.ErrorMessage) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (Success != false) hash ^= Success.GetHashCode(); + if (ErrorMessage.Length != 0) hash ^= ErrorMessage.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (Success != false) { + output.WriteRawTag(8); + output.WriteBool(Success); + } + if (ErrorMessage.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ErrorMessage); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (Success != false) { + output.WriteRawTag(8); + output.WriteBool(Success); + } + if (ErrorMessage.Length != 0) { + output.WriteRawTag(18); + output.WriteString(ErrorMessage); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (Success != false) { + size += 1 + 1; + } + if (ErrorMessage.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(ErrorMessage); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(UseSkillResponse other) { + if (other == null) { + return; + } + if (other.Success != false) { + Success = other.Success; + } + if (other.ErrorMessage.Length != 0) { + ErrorMessage = other.ErrorMessage; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + Success = input.ReadBool(); + break; + } + case 18: { + ErrorMessage = input.ReadString(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + Success = input.ReadBool(); + break; + } + case 18: { + ErrorMessage = input.ReadString(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class CombatEvent : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new CombatEvent()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[15]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public CombatEvent() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public CombatEvent(CombatEvent other) : this() { + casterId_ = other.casterId_; + targetId_ = other.targetId_; + skillId_ = other.skillId_; + damage_ = other.damage_; + heal_ = other.heal_; + isCritical_ = other.isCritical_; + targetDied_ = other.targetDied_; + targetHp_ = other.targetHp_; + targetMaxHp_ = other.targetMaxHp_; + eventType_ = other.eventType_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public CombatEvent Clone() { + return new CombatEvent(this); + } + + /// Field number for the "caster_id" field. + public const int CasterIdFieldNumber = 1; + private ulong casterId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ulong CasterId { + get { return casterId_; } + set { + casterId_ = value; + } + } + + /// Field number for the "target_id" field. + public const int TargetIdFieldNumber = 2; + private ulong targetId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ulong TargetId { + get { return targetId_; } + set { + targetId_ = value; + } + } + + /// Field number for the "skill_id" field. + public const int SkillIdFieldNumber = 3; + private uint skillId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public uint SkillId { + get { return skillId_; } + set { + skillId_ = value; + } + } + + /// Field number for the "damage" field. + public const int DamageFieldNumber = 4; + private int damage_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int Damage { + get { return damage_; } + set { + damage_ = value; + } + } + + /// Field number for the "heal" field. + public const int HealFieldNumber = 5; + private int heal_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int Heal { + get { return heal_; } + set { + heal_ = value; + } + } + + /// Field number for the "is_critical" field. + public const int IsCriticalFieldNumber = 6; + private bool isCritical_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool IsCritical { + get { return isCritical_; } + set { + isCritical_ = value; + } + } + + /// Field number for the "target_died" field. + public const int TargetDiedFieldNumber = 7; + private bool targetDied_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool TargetDied { + get { return targetDied_; } + set { + targetDied_ = value; + } + } + + /// Field number for the "target_hp" field. + public const int TargetHpFieldNumber = 8; + private int targetHp_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int TargetHp { + get { return targetHp_; } + set { + targetHp_ = value; + } + } + + /// Field number for the "target_max_hp" field. + public const int TargetMaxHpFieldNumber = 9; + private int targetMaxHp_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int TargetMaxHp { + get { return targetMaxHp_; } + set { + targetMaxHp_ = value; + } + } + + /// Field number for the "event_type" field. + public const int EventTypeFieldNumber = 10; + private global::Proto.CombatEventType eventType_ = global::Proto.CombatEventType.CombatEventDamage; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Proto.CombatEventType EventType { + get { return eventType_; } + set { + eventType_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as CombatEvent); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(CombatEvent other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (CasterId != other.CasterId) return false; + if (TargetId != other.TargetId) return false; + if (SkillId != other.SkillId) return false; + if (Damage != other.Damage) return false; + if (Heal != other.Heal) return false; + if (IsCritical != other.IsCritical) return false; + if (TargetDied != other.TargetDied) return false; + if (TargetHp != other.TargetHp) return false; + if (TargetMaxHp != other.TargetMaxHp) return false; + if (EventType != other.EventType) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (CasterId != 0UL) hash ^= CasterId.GetHashCode(); + if (TargetId != 0UL) hash ^= TargetId.GetHashCode(); + if (SkillId != 0) hash ^= SkillId.GetHashCode(); + if (Damage != 0) hash ^= Damage.GetHashCode(); + if (Heal != 0) hash ^= Heal.GetHashCode(); + if (IsCritical != false) hash ^= IsCritical.GetHashCode(); + if (TargetDied != false) hash ^= TargetDied.GetHashCode(); + if (TargetHp != 0) hash ^= TargetHp.GetHashCode(); + if (TargetMaxHp != 0) hash ^= TargetMaxHp.GetHashCode(); + if (EventType != global::Proto.CombatEventType.CombatEventDamage) hash ^= EventType.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (CasterId != 0UL) { + output.WriteRawTag(8); + output.WriteUInt64(CasterId); + } + if (TargetId != 0UL) { + output.WriteRawTag(16); + output.WriteUInt64(TargetId); + } + if (SkillId != 0) { + output.WriteRawTag(24); + output.WriteUInt32(SkillId); + } + if (Damage != 0) { + output.WriteRawTag(32); + output.WriteInt32(Damage); + } + if (Heal != 0) { + output.WriteRawTag(40); + output.WriteInt32(Heal); + } + if (IsCritical != false) { + output.WriteRawTag(48); + output.WriteBool(IsCritical); + } + if (TargetDied != false) { + output.WriteRawTag(56); + output.WriteBool(TargetDied); + } + if (TargetHp != 0) { + output.WriteRawTag(64); + output.WriteInt32(TargetHp); + } + if (TargetMaxHp != 0) { + output.WriteRawTag(72); + output.WriteInt32(TargetMaxHp); + } + if (EventType != global::Proto.CombatEventType.CombatEventDamage) { + output.WriteRawTag(80); + output.WriteEnum((int) EventType); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (CasterId != 0UL) { + output.WriteRawTag(8); + output.WriteUInt64(CasterId); + } + if (TargetId != 0UL) { + output.WriteRawTag(16); + output.WriteUInt64(TargetId); + } + if (SkillId != 0) { + output.WriteRawTag(24); + output.WriteUInt32(SkillId); + } + if (Damage != 0) { + output.WriteRawTag(32); + output.WriteInt32(Damage); + } + if (Heal != 0) { + output.WriteRawTag(40); + output.WriteInt32(Heal); + } + if (IsCritical != false) { + output.WriteRawTag(48); + output.WriteBool(IsCritical); + } + if (TargetDied != false) { + output.WriteRawTag(56); + output.WriteBool(TargetDied); + } + if (TargetHp != 0) { + output.WriteRawTag(64); + output.WriteInt32(TargetHp); + } + if (TargetMaxHp != 0) { + output.WriteRawTag(72); + output.WriteInt32(TargetMaxHp); + } + if (EventType != global::Proto.CombatEventType.CombatEventDamage) { + output.WriteRawTag(80); + output.WriteEnum((int) EventType); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (CasterId != 0UL) { + size += 1 + pb::CodedOutputStream.ComputeUInt64Size(CasterId); + } + if (TargetId != 0UL) { + size += 1 + pb::CodedOutputStream.ComputeUInt64Size(TargetId); + } + if (SkillId != 0) { + size += 1 + pb::CodedOutputStream.ComputeUInt32Size(SkillId); + } + if (Damage != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Damage); + } + if (Heal != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Heal); + } + if (IsCritical != false) { + size += 1 + 1; + } + if (TargetDied != false) { + size += 1 + 1; + } + if (TargetHp != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(TargetHp); + } + if (TargetMaxHp != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(TargetMaxHp); + } + if (EventType != global::Proto.CombatEventType.CombatEventDamage) { + size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) EventType); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(CombatEvent other) { + if (other == null) { + return; + } + if (other.CasterId != 0UL) { + CasterId = other.CasterId; + } + if (other.TargetId != 0UL) { + TargetId = other.TargetId; + } + if (other.SkillId != 0) { + SkillId = other.SkillId; + } + if (other.Damage != 0) { + Damage = other.Damage; + } + if (other.Heal != 0) { + Heal = other.Heal; + } + if (other.IsCritical != false) { + IsCritical = other.IsCritical; + } + if (other.TargetDied != false) { + TargetDied = other.TargetDied; + } + if (other.TargetHp != 0) { + TargetHp = other.TargetHp; + } + if (other.TargetMaxHp != 0) { + TargetMaxHp = other.TargetMaxHp; + } + if (other.EventType != global::Proto.CombatEventType.CombatEventDamage) { + EventType = other.EventType; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + CasterId = input.ReadUInt64(); + break; + } + case 16: { + TargetId = input.ReadUInt64(); + break; + } + case 24: { + SkillId = input.ReadUInt32(); + break; + } + case 32: { + Damage = input.ReadInt32(); + break; + } + case 40: { + Heal = input.ReadInt32(); + break; + } + case 48: { + IsCritical = input.ReadBool(); + break; + } + case 56: { + TargetDied = input.ReadBool(); + break; + } + case 64: { + TargetHp = input.ReadInt32(); + break; + } + case 72: { + TargetMaxHp = input.ReadInt32(); + break; + } + case 80: { + EventType = (global::Proto.CombatEventType) input.ReadEnum(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + CasterId = input.ReadUInt64(); + break; + } + case 16: { + TargetId = input.ReadUInt64(); + break; + } + case 24: { + SkillId = input.ReadUInt32(); + break; + } + case 32: { + Damage = input.ReadInt32(); + break; + } + case 40: { + Heal = input.ReadInt32(); + break; + } + case 48: { + IsCritical = input.ReadBool(); + break; + } + case 56: { + TargetDied = input.ReadBool(); + break; + } + case 64: { + TargetHp = input.ReadInt32(); + break; + } + case 72: { + TargetMaxHp = input.ReadInt32(); + break; + } + case 80: { + EventType = (global::Proto.CombatEventType) input.ReadEnum(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class BuffApplied : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new BuffApplied()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[16]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public BuffApplied() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public BuffApplied(BuffApplied other) : this() { + targetId_ = other.targetId_; + buffId_ = other.buffId_; + buffName_ = other.buffName_; + duration_ = other.duration_; + isDebuff_ = other.isDebuff_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public BuffApplied Clone() { + return new BuffApplied(this); + } + + /// Field number for the "target_id" field. + public const int TargetIdFieldNumber = 1; + private ulong targetId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ulong TargetId { + get { return targetId_; } + set { + targetId_ = value; + } + } + + /// Field number for the "buff_id" field. + public const int BuffIdFieldNumber = 2; + private uint buffId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public uint BuffId { + get { return buffId_; } + set { + buffId_ = value; + } + } + + /// Field number for the "buff_name" field. + public const int BuffNameFieldNumber = 3; + private string buffName_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string BuffName { + get { return buffName_; } + set { + buffName_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + /// Field number for the "duration" field. + public const int DurationFieldNumber = 4; + private float duration_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public float Duration { + get { return duration_; } + set { + duration_ = value; + } + } + + /// Field number for the "is_debuff" field. + public const int IsDebuffFieldNumber = 5; + private bool isDebuff_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool IsDebuff { + get { return isDebuff_; } + set { + isDebuff_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as BuffApplied); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(BuffApplied other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (TargetId != other.TargetId) return false; + if (BuffId != other.BuffId) return false; + if (BuffName != other.BuffName) return false; + if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(Duration, other.Duration)) return false; + if (IsDebuff != other.IsDebuff) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (TargetId != 0UL) hash ^= TargetId.GetHashCode(); + if (BuffId != 0) hash ^= BuffId.GetHashCode(); + if (BuffName.Length != 0) hash ^= BuffName.GetHashCode(); + if (Duration != 0F) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(Duration); + if (IsDebuff != false) hash ^= IsDebuff.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (TargetId != 0UL) { + output.WriteRawTag(8); + output.WriteUInt64(TargetId); + } + if (BuffId != 0) { + output.WriteRawTag(16); + output.WriteUInt32(BuffId); + } + if (BuffName.Length != 0) { + output.WriteRawTag(26); + output.WriteString(BuffName); + } + if (Duration != 0F) { + output.WriteRawTag(37); + output.WriteFloat(Duration); + } + if (IsDebuff != false) { + output.WriteRawTag(40); + output.WriteBool(IsDebuff); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (TargetId != 0UL) { + output.WriteRawTag(8); + output.WriteUInt64(TargetId); + } + if (BuffId != 0) { + output.WriteRawTag(16); + output.WriteUInt32(BuffId); + } + if (BuffName.Length != 0) { + output.WriteRawTag(26); + output.WriteString(BuffName); + } + if (Duration != 0F) { + output.WriteRawTag(37); + output.WriteFloat(Duration); + } + if (IsDebuff != false) { + output.WriteRawTag(40); + output.WriteBool(IsDebuff); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (TargetId != 0UL) { + size += 1 + pb::CodedOutputStream.ComputeUInt64Size(TargetId); + } + if (BuffId != 0) { + size += 1 + pb::CodedOutputStream.ComputeUInt32Size(BuffId); + } + if (BuffName.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(BuffName); + } + if (Duration != 0F) { + size += 1 + 4; + } + if (IsDebuff != false) { + size += 1 + 1; + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(BuffApplied other) { + if (other == null) { + return; + } + if (other.TargetId != 0UL) { + TargetId = other.TargetId; + } + if (other.BuffId != 0) { + BuffId = other.BuffId; + } + if (other.BuffName.Length != 0) { + BuffName = other.BuffName; + } + if (other.Duration != 0F) { + Duration = other.Duration; + } + if (other.IsDebuff != false) { + IsDebuff = other.IsDebuff; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + TargetId = input.ReadUInt64(); + break; + } + case 16: { + BuffId = input.ReadUInt32(); + break; + } + case 26: { + BuffName = input.ReadString(); + break; + } + case 37: { + Duration = input.ReadFloat(); + break; + } + case 40: { + IsDebuff = input.ReadBool(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + TargetId = input.ReadUInt64(); + break; + } + case 16: { + BuffId = input.ReadUInt32(); + break; + } + case 26: { + BuffName = input.ReadString(); + break; + } + case 37: { + Duration = input.ReadFloat(); + break; + } + case 40: { + IsDebuff = input.ReadBool(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class BuffRemoved : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new BuffRemoved()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[17]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public BuffRemoved() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public BuffRemoved(BuffRemoved other) : this() { + targetId_ = other.targetId_; + buffId_ = other.buffId_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public BuffRemoved Clone() { + return new BuffRemoved(this); + } + + /// Field number for the "target_id" field. + public const int TargetIdFieldNumber = 1; + private ulong targetId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ulong TargetId { + get { return targetId_; } + set { + targetId_ = value; + } + } + + /// Field number for the "buff_id" field. + public const int BuffIdFieldNumber = 2; + private uint buffId_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public uint BuffId { + get { return buffId_; } + set { + buffId_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as BuffRemoved); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(BuffRemoved other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (TargetId != other.TargetId) return false; + if (BuffId != other.BuffId) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (TargetId != 0UL) hash ^= TargetId.GetHashCode(); + if (BuffId != 0) hash ^= BuffId.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (TargetId != 0UL) { + output.WriteRawTag(8); + output.WriteUInt64(TargetId); + } + if (BuffId != 0) { + output.WriteRawTag(16); + output.WriteUInt32(BuffId); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (TargetId != 0UL) { + output.WriteRawTag(8); + output.WriteUInt64(TargetId); + } + if (BuffId != 0) { + output.WriteRawTag(16); + output.WriteUInt32(BuffId); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (TargetId != 0UL) { + size += 1 + pb::CodedOutputStream.ComputeUInt64Size(TargetId); + } + if (BuffId != 0) { + size += 1 + pb::CodedOutputStream.ComputeUInt32Size(BuffId); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(BuffRemoved other) { + if (other == null) { + return; + } + if (other.TargetId != 0UL) { + TargetId = other.TargetId; + } + if (other.BuffId != 0) { + BuffId = other.BuffId; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + TargetId = input.ReadUInt64(); + break; + } + case 16: { + BuffId = input.ReadUInt32(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + TargetId = input.ReadUInt64(); + break; + } + case 16: { + BuffId = input.ReadUInt32(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class RespawnRequest : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new RespawnRequest()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[18]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public RespawnRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public RespawnRequest(RespawnRequest other) : this() { + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public RespawnRequest Clone() { + return new RespawnRequest(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as RespawnRequest); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(RespawnRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(RespawnRequest other) { + if (other == null) { + return; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class RespawnResponse : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new RespawnResponse()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[19]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public RespawnResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public RespawnResponse(RespawnResponse other) : this() { + self_ = other.self_ != null ? other.self_.Clone() : null; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public RespawnResponse Clone() { + return new RespawnResponse(this); + } + + /// Field number for the "self" field. + public const int SelfFieldNumber = 1; + private global::Proto.EntityState self_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public global::Proto.EntityState Self { + get { return self_; } + set { + self_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as RespawnResponse); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(RespawnResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (!object.Equals(Self, other.Self)) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (self_ != null) hash ^= Self.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (self_ != null) { + output.WriteRawTag(10); + output.WriteMessage(Self); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (self_ != null) { + output.WriteRawTag(10); + output.WriteMessage(Self); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (self_ != null) { + size += 1 + pb::CodedOutputStream.ComputeMessageSize(Self); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(RespawnResponse other) { + if (other == null) { + return; + } + if (other.self_ != null) { + if (self_ == null) { + Self = new global::Proto.EntityState(); + } + Self.MergeFrom(other.Self); + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 10: { + if (self_ == null) { + Self = new global::Proto.EntityState(); + } + input.ReadMessage(Self); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 10: { + if (self_ == null) { + Self = new global::Proto.EntityState(); + } + input.ReadMessage(Self); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class AOIToggleRequest : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new AOIToggleRequest()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[20]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public AOIToggleRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public AOIToggleRequest(AOIToggleRequest other) : this() { + enabled_ = other.enabled_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public AOIToggleRequest Clone() { + return new AOIToggleRequest(this); + } + + /// Field number for the "enabled" field. + public const int EnabledFieldNumber = 1; + private bool enabled_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Enabled { + get { return enabled_; } + set { + enabled_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as AOIToggleRequest); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(AOIToggleRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Enabled != other.Enabled) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (Enabled != false) hash ^= Enabled.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (Enabled != false) { + output.WriteRawTag(8); + output.WriteBool(Enabled); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (Enabled != false) { + output.WriteRawTag(8); + output.WriteBool(Enabled); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (Enabled != false) { + size += 1 + 1; + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(AOIToggleRequest other) { + if (other == null) { + return; + } + if (other.Enabled != false) { + Enabled = other.Enabled; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + Enabled = input.ReadBool(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + Enabled = input.ReadBool(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class AOIToggleResponse : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new AOIToggleResponse()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[21]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public AOIToggleResponse() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public AOIToggleResponse(AOIToggleResponse other) : this() { + enabled_ = other.enabled_; + message_ = other.message_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public AOIToggleResponse Clone() { + return new AOIToggleResponse(this); + } + + /// Field number for the "enabled" field. + public const int EnabledFieldNumber = 1; + private bool enabled_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Enabled { + get { return enabled_; } + set { + enabled_ = value; + } + } + + /// Field number for the "message" field. + public const int MessageFieldNumber = 2; + private string message_ = ""; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public string Message { + get { return message_; } + set { + message_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as AOIToggleResponse); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(AOIToggleResponse other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Enabled != other.Enabled) return false; + if (Message != other.Message) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (Enabled != false) hash ^= Enabled.GetHashCode(); + if (Message.Length != 0) hash ^= Message.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (Enabled != false) { + output.WriteRawTag(8); + output.WriteBool(Enabled); + } + if (Message.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Message); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (Enabled != false) { + output.WriteRawTag(8); + output.WriteBool(Enabled); + } + if (Message.Length != 0) { + output.WriteRawTag(18); + output.WriteString(Message); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (Enabled != false) { + size += 1 + 1; + } + if (Message.Length != 0) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(Message); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(AOIToggleResponse other) { + if (other == null) { + return; + } + if (other.Enabled != false) { + Enabled = other.Enabled; + } + if (other.Message.Length != 0) { + Message = other.Message; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + Enabled = input.ReadBool(); + break; + } + case 18: { + Message = input.ReadString(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + Enabled = input.ReadBool(); + break; + } + case 18: { + Message = input.ReadString(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class ServerMetrics : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new ServerMetrics()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[22]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ServerMetrics() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ServerMetrics(ServerMetrics other) : this() { + onlinePlayers_ = other.onlinePlayers_; + totalEntities_ = other.totalEntities_; + tickDurationUs_ = other.tickDurationUs_; + aoiEnabled_ = other.aoiEnabled_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public ServerMetrics Clone() { + return new ServerMetrics(this); + } + + /// Field number for the "online_players" field. + public const int OnlinePlayersFieldNumber = 1; + private int onlinePlayers_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int OnlinePlayers { + get { return onlinePlayers_; } + set { + onlinePlayers_ = value; + } + } + + /// Field number for the "total_entities" field. + public const int TotalEntitiesFieldNumber = 2; + private int totalEntities_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int TotalEntities { + get { return totalEntities_; } + set { + totalEntities_ = value; + } + } + + /// Field number for the "tick_duration_us" field. + public const int TickDurationUsFieldNumber = 3; + private long tickDurationUs_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public long TickDurationUs { + get { return tickDurationUs_; } + set { + tickDurationUs_ = value; + } + } + + /// Field number for the "aoi_enabled" field. + public const int AoiEnabledFieldNumber = 4; + private bool aoiEnabled_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool AoiEnabled { + get { return aoiEnabled_; } + set { + aoiEnabled_ = value; + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as ServerMetrics); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(ServerMetrics other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (OnlinePlayers != other.OnlinePlayers) return false; + if (TotalEntities != other.TotalEntities) return false; + if (TickDurationUs != other.TickDurationUs) return false; + if (AoiEnabled != other.AoiEnabled) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (OnlinePlayers != 0) hash ^= OnlinePlayers.GetHashCode(); + if (TotalEntities != 0) hash ^= TotalEntities.GetHashCode(); + if (TickDurationUs != 0L) hash ^= TickDurationUs.GetHashCode(); + if (AoiEnabled != false) hash ^= AoiEnabled.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (OnlinePlayers != 0) { + output.WriteRawTag(8); + output.WriteInt32(OnlinePlayers); + } + if (TotalEntities != 0) { + output.WriteRawTag(16); + output.WriteInt32(TotalEntities); + } + if (TickDurationUs != 0L) { + output.WriteRawTag(24); + output.WriteInt64(TickDurationUs); + } + if (AoiEnabled != false) { + output.WriteRawTag(32); + output.WriteBool(AoiEnabled); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (OnlinePlayers != 0) { + output.WriteRawTag(8); + output.WriteInt32(OnlinePlayers); + } + if (TotalEntities != 0) { + output.WriteRawTag(16); + output.WriteInt32(TotalEntities); + } + if (TickDurationUs != 0L) { + output.WriteRawTag(24); + output.WriteInt64(TickDurationUs); + } + if (AoiEnabled != false) { + output.WriteRawTag(32); + output.WriteBool(AoiEnabled); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (OnlinePlayers != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(OnlinePlayers); + } + if (TotalEntities != 0) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(TotalEntities); + } + if (TickDurationUs != 0L) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(TickDurationUs); + } + if (AoiEnabled != false) { + size += 1 + 1; + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(ServerMetrics other) { + if (other == null) { + return; + } + if (other.OnlinePlayers != 0) { + OnlinePlayers = other.OnlinePlayers; + } + if (other.TotalEntities != 0) { + TotalEntities = other.TotalEntities; + } + if (other.TickDurationUs != 0L) { + TickDurationUs = other.TickDurationUs; + } + if (other.AoiEnabled != false) { + AoiEnabled = other.AoiEnabled; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + OnlinePlayers = input.ReadInt32(); + break; + } + case 16: { + TotalEntities = input.ReadInt32(); + break; + } + case 24: { + TickDurationUs = input.ReadInt64(); + break; + } + case 32: { + AoiEnabled = input.ReadBool(); + break; + } + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + case 8: { + OnlinePlayers = input.ReadInt32(); + break; + } + case 16: { + TotalEntities = input.ReadInt32(); + break; + } + case 24: { + TickDurationUs = input.ReadInt64(); + break; + } + case 32: { + AoiEnabled = input.ReadBool(); + break; + } + } + } + } + #endif + + } + + [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] + public sealed partial class MetricsRequest : pb::IMessage + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + , pb::IBufferMessage + #endif + { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new MetricsRequest()); + private pb::UnknownFieldSet _unknownFields; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public static pbr::MessageDescriptor Descriptor { + get { return global::Proto.MessagesReflection.Descriptor.MessageTypes[23]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public MetricsRequest() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public MetricsRequest(MetricsRequest other) : this() { + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public MetricsRequest Clone() { + return new MetricsRequest(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override bool Equals(object other) { + return Equals(other as MetricsRequest); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public bool Equals(MetricsRequest other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override int GetHashCode() { + int hash = 1; + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void WriteTo(pb::CodedOutputStream output) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + output.WriteRawMessage(this); + #else + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { + if (_unknownFields != null) { + _unknownFields.WriteTo(ref output); + } + } + #endif + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public int CalculateSize() { + int size = 0; + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(MetricsRequest other) { + if (other == null) { + return; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + public void MergeFrom(pb::CodedInputStream input) { + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + input.ReadRawMessage(this); + #else + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + } + } + #endif + } + + #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] + void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + if ((tag & 7) == 4) { + // Abort on any end group tag. + return; + } + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); + break; + } + } + } + #endif + + } + + #endregion + +} + +#endregion Designer generated code