From 14cf8fe1876445fc85a111ab81111869bc24fb26 Mon Sep 17 00:00:00 2001 From: dal4segno Date: Tue, 10 Mar 2026 16:44:14 +0900 Subject: [PATCH] =?UTF-8?q?[Network]=20=EC=8A=A4=ED=82=AC=20=EC=8B=9C?= =?UTF-8?q?=EC=8A=A4=ED=85=9C=20=EB=84=A4=ED=8A=B8=EC=9B=8C=ED=81=AC=20?= =?UTF-8?q?=EB=8F=99=EA=B8=B0=ED=99=94=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 스킬 실행 RequestSkillExecutionRpc (Client -> Server) - 스킬 전파 BroadcastSkillExecutionRpc (Server -> All Clients) - 서버에서 마나/쿨타임 검증 - NormalizeTime 디버그 로그 제거 --- Assets/Scenes/Test.unity | 26 ++++- Assets/Scripts/Player/PlayerSkillInput.cs | 65 +++++++++-- Assets/Scripts/Skills/SkillController.cs | 5 - Assets/UserChoices.choices | 36 ++++++ Assets/UserChoices.choices.meta | 7 ++ Packages/manifest.json | 4 + Packages/packages-lock.json | 107 ++++++++++++++++++ ProjectSettings/MultiplayerManager.asset | 8 +- .../ContentSelectionSettings.asset | 22 ++++ ProjectSettings/VirtualProjectsConfig.json | 4 + 10 files changed, 267 insertions(+), 17 deletions(-) create mode 100644 Assets/UserChoices.choices create mode 100644 Assets/UserChoices.choices.meta create mode 100644 ProjectSettings/Packages/com.unity.dedicated-server/ContentSelectionSettings.asset create mode 100644 ProjectSettings/VirtualProjectsConfig.json diff --git a/Assets/Scenes/Test.unity b/Assets/Scenes/Test.unity index 0fda99c4..f09de158 100644 --- a/Assets/Scenes/Test.unity +++ b/Assets/Scenes/Test.unity @@ -261,6 +261,18 @@ PrefabInstance: propertyPath: m_SizeDelta.y value: 80 objectReference: {fileID: 0} + - target: {fileID: 1384280946776679044, guid: 99f359b6678b0064dbd20508482d6d64, type: 3} + propertyPath: m_LocalScale.x + value: 0.8 + objectReference: {fileID: 0} + - target: {fileID: 1384280946776679044, guid: 99f359b6678b0064dbd20508482d6d64, type: 3} + propertyPath: m_LocalScale.y + value: 0.8 + objectReference: {fileID: 0} + - target: {fileID: 1384280946776679044, guid: 99f359b6678b0064dbd20508482d6d64, type: 3} + propertyPath: m_LocalScale.z + value: 1 + objectReference: {fileID: 0} - target: {fileID: 1384280946776679044, guid: 99f359b6678b0064dbd20508482d6d64, type: 3} propertyPath: m_LocalPosition.x value: 0 @@ -295,7 +307,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 1384280946776679044, guid: 99f359b6678b0064dbd20508482d6d64, type: 3} propertyPath: m_AnchoredPosition.y - value: -420 + value: -430 objectReference: {fileID: 0} - target: {fileID: 1384280946776679044, guid: 99f359b6678b0064dbd20508482d6d64, type: 3} propertyPath: m_LocalEulerAnglesHint.x @@ -406,6 +418,18 @@ PrefabInstance: propertyPath: m_SizeDelta.y value: 80 objectReference: {fileID: 0} + - target: {fileID: 1384280946776679044, guid: d8795051068c4f84e84c227a6618e587, type: 3} + propertyPath: m_LocalScale.x + value: 0.8 + objectReference: {fileID: 0} + - target: {fileID: 1384280946776679044, guid: d8795051068c4f84e84c227a6618e587, type: 3} + propertyPath: m_LocalScale.y + value: 0.8 + objectReference: {fileID: 0} + - target: {fileID: 1384280946776679044, guid: d8795051068c4f84e84c227a6618e587, type: 3} + propertyPath: m_LocalScale.z + value: 1 + objectReference: {fileID: 0} - target: {fileID: 1384280946776679044, guid: d8795051068c4f84e84c227a6618e587, type: 3} propertyPath: m_LocalPosition.x value: 0 diff --git a/Assets/Scripts/Player/PlayerSkillInput.cs b/Assets/Scripts/Player/PlayerSkillInput.cs index 2272534f..4bdf5164 100644 --- a/Assets/Scripts/Player/PlayerSkillInput.cs +++ b/Assets/Scripts/Player/PlayerSkillInput.cs @@ -97,26 +97,73 @@ namespace Colosseum.Player return; } + // 로컬 체크 (빠른 피드백용) + if (skillController.IsExecutingSkill) + { + Debug.Log($"Already executing skill"); + return; + } + + if (skillController.IsOnCooldown(skill)) + { + Debug.Log($"Skill {skill.SkillName} is on cooldown"); + return; + } + // 마나 비용 체크 if (networkController != null && networkController.Mana < skill.ManaCost) { - Debug.Log($"Not enough mana for skill: {skill.SkillName} (Required: {skill.ManaCost}, Current: {networkController.Mana})"); + Debug.Log($"Not enough mana for skill: {skill.SkillName}"); return; } - // 논타겟: 타겟 없이 스킬 시전 - bool success = skillController.ExecuteSkill(skill); - if (!success) - { - Debug.Log($"Cannot execute skill: {skill.SkillName}"); - return; - } + // 서버에 스킬 실행 요청 + RequestSkillExecutionRpc(slotIndex); + } - // 스킬 성공 시 마나 소모 + /// + /// 서버에 스킬 실행 요청 + /// + [Rpc(SendTo.Server)] + private void RequestSkillExecutionRpc(int slotIndex) + { + if (slotIndex < 0 || slotIndex >= skillSlots.Length) + return; + + SkillData skill = skillSlots[slotIndex]; + if (skill == null) return; + + // 서버에서 다시 검증 + if (skillController.IsExecutingSkill || skillController.IsOnCooldown(skill)) + return; + + if (networkController != null && networkController.Mana < skill.ManaCost) + return; + + // 마나 소모 if (networkController != null && skill.ManaCost > 0) { networkController.UseManaRpc(skill.ManaCost); } + + // 모든 클라이언트에 스킬 실행 전파 + BroadcastSkillExecutionRpc(slotIndex); + } + + /// + /// 모든 클라이언트에 스킬 실행 전파 + /// + [Rpc(SendTo.ClientsAndHost)] + private void BroadcastSkillExecutionRpc(int slotIndex) + { + if (slotIndex < 0 || slotIndex >= skillSlots.Length) + return; + + SkillData skill = skillSlots[slotIndex]; + if (skill == null) return; + + // 모든 클라이언트에서 스킬 실행 (애니메이션 포함) + skillController.ExecuteSkill(skill); } /// diff --git a/Assets/Scripts/Skills/SkillController.cs b/Assets/Scripts/Skills/SkillController.cs index 75146e10..628f6536 100644 --- a/Assets/Scripts/Skills/SkillController.cs +++ b/Assets/Scripts/Skills/SkillController.cs @@ -61,11 +61,6 @@ namespace Colosseum.Skills var stateInfo = animator.GetCurrentAnimatorStateInfo(0); - if (debugMode) - { - Debug.Log($"[Skill] State: {stateInfo.shortNameHash}, NormalizedTime: {stateInfo.normalizedTime:F2}, IsSkill: {stateInfo.IsName(SKILL_STATE_NAME)}"); - } - // EndAnimation 종료 감지 if (waitingForEndAnimation) { diff --git a/Assets/UserChoices.choices b/Assets/UserChoices.choices new file mode 100644 index 00000000..3532d60e --- /dev/null +++ b/Assets/UserChoices.choices @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 53 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 18cde282a8d045bf9d245fdcfaa7271b, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.Multiplayer.Center.Editor::Unity.Multiplayer.Center.Questionnaire.UserChoicesObject + QuestionnaireVersion: 1.3 + UserAnswers: + Answers: + - QuestionId: Pace + Answers: + - Fast + - QuestionId: Cheating + Answers: + - CheatingImportant + - QuestionId: CostSensitivity + Answers: + - BestExperience + - QuestionId: NetcodeArchitecture + Answers: + - ClientServer + - QuestionId: PlayerCount + Answers: + - 4 + Preset: 2 + SelectedSolutions: + SelectedHostingModel: 2 + SelectedNetcodeSolution: 1 diff --git a/Assets/UserChoices.choices.meta b/Assets/UserChoices.choices.meta new file mode 100644 index 00000000..d89f3481 --- /dev/null +++ b/Assets/UserChoices.choices.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 291abe421e975b24d9468acf16de7b2e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/manifest.json b/Packages/manifest.json index cd432de1..f7ed6a14 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -2,13 +2,17 @@ "dependencies": { "com.unity.ai.navigation": "2.0.10", "com.unity.collab-proxy": "2.11.3", + "com.unity.dedicated-server": "2.0.1", "com.unity.ide.rider": "3.0.39", "com.unity.ide.visualstudio": "2.0.26", "com.unity.inputsystem": "1.18.0", "com.unity.multiplayer.center": "1.0.1", + "com.unity.multiplayer.center.quickstart": "1.1.1", + "com.unity.multiplayer.playmode": "2.0.1", "com.unity.multiplayer.tools": "2.2.8", "com.unity.netcode.gameobjects": "2.10.0", "com.unity.render-pipelines.universal": "17.3.0", + "com.unity.services.multiplayer": "2.1.3", "com.unity.test-framework": "1.6.0", "com.unity.timeline": "1.8.10", "com.unity.ugui": "2.0.0", diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index a9234ebc..5d8ba586 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -39,6 +39,13 @@ }, "url": "https://packages.unity.com" }, + "com.unity.dedicated-server": { + "version": "2.0.1", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, "com.unity.ext.nunit": { "version": "2.0.5", "depth": 1, @@ -87,6 +94,25 @@ "com.unity.modules.uielements": "1.0.0" } }, + "com.unity.multiplayer.center.quickstart": { + "version": "1.1.1", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.uielements": "1.0.0", + "com.unity.multiplayer.center": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.multiplayer.playmode": { + "version": "2.0.1", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.nuget.newtonsoft-json": "2.0.2" + }, + "url": "https://packages.unity.com" + }, "com.unity.multiplayer.tools": { "version": "2.2.8", "depth": 0, @@ -172,6 +198,87 @@ "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.services.authentication": { + "version": "3.6.0", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0", + "com.unity.services.core": "1.15.1", + "com.unity.nuget.newtonsoft-json": "3.2.1", + "com.unity.modules.unitywebrequest": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.services.core": { + "version": "1.16.0", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.modules.androidjni": "1.0.0", + "com.unity.nuget.newtonsoft-json": "3.2.1", + "com.unity.modules.unitywebrequest": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.services.deployment": { + "version": "1.7.1", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.services.core": "1.15.1", + "com.unity.services.deployment.api": "1.1.2" + }, + "url": "https://packages.unity.com" + }, + "com.unity.services.deployment.api": { + "version": "1.1.3", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.services.multiplayer": { + "version": "2.1.3", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.transport": "2.6.0", + "com.unity.collections": "2.2.1", + "com.unity.services.qos": "1.4.1", + "com.unity.services.core": "1.16.0", + "com.unity.services.wire": "1.4.1", + "com.unity.services.deployment": "1.7.1", + "com.unity.nuget.newtonsoft-json": "3.2.2", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.services.authentication": "3.6.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.services.qos": { + "version": "1.4.1", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.collections": "1.2.4", + "com.unity.services.core": "1.12.5", + "com.unity.nuget.newtonsoft-json": "3.0.2", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.services.authentication": "3.5.2" + }, + "url": "https://packages.unity.com" + }, + "com.unity.services.wire": { + "version": "1.4.1", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.services.core": "1.12.5", + "com.unity.nuget.newtonsoft-json": "3.2.1", + "com.unity.services.authentication": "2.7.4" + }, + "url": "https://packages.unity.com" + }, "com.unity.shadergraph": { "version": "17.3.0", "depth": 1, diff --git a/ProjectSettings/MultiplayerManager.asset b/ProjectSettings/MultiplayerManager.asset index 2a936644..f9820a8b 100644 --- a/ProjectSettings/MultiplayerManager.asset +++ b/ProjectSettings/MultiplayerManager.asset @@ -3,5 +3,9 @@ --- !u!655991488 &1 MultiplayerManager: m_ObjectHideFlags: 0 - m_EnableMultiplayerRoles: 0 - m_StrippingTypes: {} + m_EnableMultiplayerRoles: 1 + m_EnablePlayModeLocalDeployment: 0 + m_EnablePlayModeRemoteDeployment: 0 + m_StrippingTypes: + 1: [] + 2: [] diff --git a/ProjectSettings/Packages/com.unity.dedicated-server/ContentSelectionSettings.asset b/ProjectSettings/Packages/com.unity.dedicated-server/ContentSelectionSettings.asset new file mode 100644 index 00000000..565a173a --- /dev/null +++ b/ProjectSettings/Packages/com.unity.dedicated-server/ContentSelectionSettings.asset @@ -0,0 +1,22 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 53 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d3c63bcbf0c7c4315b6f985026440942, type: 3} + m_Name: + m_EditorClassIdentifier: Unity.DedicatedServer.MultiplayerRoles.Editor::Unity.Multiplayer.Editor.ContentSelectionSettings + m_EnableSafetyChecks: 1 + m_AutomaticSelectionOptions: + m_StripRenderComponents: 0 + m_StripUIComponents: 0 + m_StripAudioComponents: 0 + m_CustomComponentsList: + m_Keys: [] + m_Values: diff --git a/ProjectSettings/VirtualProjectsConfig.json b/ProjectSettings/VirtualProjectsConfig.json new file mode 100644 index 00000000..5dd584d7 --- /dev/null +++ b/ProjectSettings/VirtualProjectsConfig.json @@ -0,0 +1,4 @@ +{ + "PlayerTags": [], + "version": "6000.3.10f1" +} \ No newline at end of file