basic developer console
This commit is contained in:
parent
4375350901
commit
04d7b42e60
4 changed files with 184 additions and 8 deletions
|
|
@ -18,6 +18,7 @@ GameObject:
|
||||||
- component: {fileID: 2806524246861401801}
|
- component: {fileID: 2806524246861401801}
|
||||||
- component: {fileID: 5683786710272852339}
|
- component: {fileID: 5683786710272852339}
|
||||||
- component: {fileID: 6856236869205671849}
|
- component: {fileID: 6856236869205671849}
|
||||||
|
- component: {fileID: -4438158474568445526}
|
||||||
m_Layer: 0
|
m_Layer: 0
|
||||||
m_Name: Player
|
m_Name: Player
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
|
|
@ -173,3 +174,16 @@ MonoBehaviour:
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PlayerSpellLoadout
|
m_EditorClassIdentifier: Assembly-CSharp::TD.Gameplay.PlayerSpellLoadout
|
||||||
ShowTopMostFoldoutHeaderGroup: 1
|
ShowTopMostFoldoutHeaderGroup: 1
|
||||||
|
--- !u!114 &-4438158474568445526
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 3493329038866903420}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: 65825b3189ae873948659b37b219f5e6, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::TD.Dev.DebugCommandRelay
|
||||||
|
ShowTopMostFoldoutHeaderGroup: 1
|
||||||
|
|
|
||||||
78
Assets/_Project/Scripts/Dev/DebugCommandRelay.cs
Normal file
78
Assets/_Project/Scripts/Dev/DebugCommandRelay.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Assets/_Project/Scripts/Dev/DebugCommandRelay.cs
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Unity.Netcode;
|
||||||
|
using UnityEngine;
|
||||||
|
using TD.Gameplay.Draft;
|
||||||
|
|
||||||
|
namespace TD.Dev
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Debug-only network relay: exposes one RPC that force-applies a <see cref="DraftOption"/>
|
||||||
|
/// by its <see cref="DraftPool"/> index, bypassing <c>PlayerDraft</c>'s "must currently be
|
||||||
|
/// offered" check.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Lives entirely separate from PlayerDraft/DraftOption/DraftPool — none of those files
|
||||||
|
/// know this component exists. Removing this component (and TD.Dev.DebugConsole, which
|
||||||
|
/// calls it) leaves the shipping draft system completely untouched.
|
||||||
|
///
|
||||||
|
/// Added to the Player prefab alongside PlayerDraft/PlayerGoldManager/etc. so it spawns
|
||||||
|
/// with a per-player OwnerClientId. Static per-client registry mirrors PlayerDraft's
|
||||||
|
/// GetForClient/Local pattern.
|
||||||
|
/// </remarks>
|
||||||
|
public class DebugCommandRelay : NetworkBehaviour
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<ulong, DebugCommandRelay> s_byClientId
|
||||||
|
= new Dictionary<ulong, DebugCommandRelay>();
|
||||||
|
|
||||||
|
public static DebugCommandRelay GetForClient(ulong clientId)
|
||||||
|
{
|
||||||
|
s_byClientId.TryGetValue(clientId, out var relay);
|
||||||
|
return relay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DebugCommandRelay Local
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var nm = NetworkManager.Singleton;
|
||||||
|
if (nm == null || !nm.IsClient) return null;
|
||||||
|
return GetForClient(nm.LocalClientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnNetworkSpawn()
|
||||||
|
{
|
||||||
|
s_byClientId[OwnerClientId] = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnNetworkDespawn()
|
||||||
|
{
|
||||||
|
if (s_byClientId.TryGetValue(OwnerClientId, out var registered) && registered == this)
|
||||||
|
s_byClientId.Remove(OwnerClientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Owning client: force-apply the DraftOption at <paramref name="optionId"/> (its
|
||||||
|
/// DraftPool index) immediately, skipping the normal "must be currently offered" gate.
|
||||||
|
/// Debug/testing only — any connected client can grant themselves anything via this
|
||||||
|
/// RPC, same tradeoff DevWaveControls already accepts for its hotkeys.
|
||||||
|
/// </summary>
|
||||||
|
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Owner)]
|
||||||
|
public void DebugGrantDraftOptionRpc(int optionId)
|
||||||
|
{
|
||||||
|
var option = DraftPool.Instance != null ? DraftPool.Instance.Get(optionId) : null;
|
||||||
|
if (option == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[DebugCommandRelay] Grant requested for unknown option id {optionId}.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (option.ServerApply(OwnerClientId))
|
||||||
|
Debug.Log($"[DebugCommandRelay] Granted '{option.DisplayName}' to client {OwnerClientId}.");
|
||||||
|
else
|
||||||
|
Debug.LogWarning($"[DebugCommandRelay] '{option.DisplayName}' could not be applied " +
|
||||||
|
$"to client {OwnerClientId}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Assets/_Project/Scripts/Dev/DebugCommandRelay.cs.meta
Normal file
2
Assets/_Project/Scripts/Dev/DebugCommandRelay.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 65825b3189ae873948659b37b219f5e6
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
// Assets/_Project/Scripts/Dev/DebugConsole.cs
|
// Assets/_Project/Scripts/Dev/DebugConsole.cs
|
||||||
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.InputSystem;
|
using UnityEngine.InputSystem;
|
||||||
using UnityEngine.UIElements;
|
using UnityEngine.UIElements;
|
||||||
using TD.UI;
|
using TD.UI;
|
||||||
|
using TD.Gameplay.Draft;
|
||||||
|
|
||||||
namespace TD.Dev
|
namespace TD.Dev
|
||||||
{
|
{
|
||||||
|
|
@ -17,9 +21,9 @@ namespace TD.Dev
|
||||||
/// a build entirely by not including that GameObject, the same way
|
/// a build entirely by not including that GameObject, the same way
|
||||||
/// <see cref="DevWaveControls"/> is kept separate from shipping systems.
|
/// <see cref="DevWaveControls"/> is kept separate from shipping systems.
|
||||||
///
|
///
|
||||||
/// This first pass only wires up open/close/focus/text-capture. Submit
|
/// Supports one command so far: "get <type> <name>" grants a
|
||||||
/// currently just logs the typed text — command parsing/dispatch (e.g.
|
/// <see cref="TD.Gameplay.Draft.DraftOption"/> to the local player via
|
||||||
/// unlocking spells) comes next.
|
/// <see cref="DebugCommandRelay"/>.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[RequireComponent(typeof(UIDocument))]
|
[RequireComponent(typeof(UIDocument))]
|
||||||
public class DebugConsole : MonoBehaviour
|
public class DebugConsole : MonoBehaviour
|
||||||
|
|
@ -204,15 +208,93 @@ namespace TD.Dev
|
||||||
HUDController.SuppressChatOpenUntilFrame = Time.frameCount + 1;
|
HUDController.SuppressChatOpenUntilFrame = Time.frameCount + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder — command parsing/dispatch (unlocking spells, granting gold,
|
|
||||||
// etc.) comes next. For now this just proves the text made it through.
|
|
||||||
private void SubmitCommand()
|
private void SubmitCommand()
|
||||||
{
|
{
|
||||||
string text = commandInput?.value ?? string.Empty;
|
string text = commandInput?.value ?? string.Empty;
|
||||||
if (!string.IsNullOrWhiteSpace(text))
|
|
||||||
Debug.Log($"[DebugConsole] Command entered: \"{text}\"");
|
|
||||||
|
|
||||||
CloseConsole();
|
CloseConsole();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(text)) return;
|
||||||
|
ExecuteCommand(text.Trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps the type token in "get <type> <name>" to the DraftOption subclass it
|
||||||
|
// should match against. Add an entry here whenever a new DraftOption subclass
|
||||||
|
// should be gettable from the console.
|
||||||
|
private static readonly Dictionary<string, Type> DraftTypeAliases =
|
||||||
|
new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ "spell", typeof(BuilderSpellDraftOption) },
|
||||||
|
{ "tower", typeof(NewTowerDraftOption) },
|
||||||
|
{ "buff", typeof(BuilderEffectDraftOption) },
|
||||||
|
{ "effect", typeof(BuilderEffectDraftOption) },
|
||||||
|
};
|
||||||
|
|
||||||
|
private void ExecuteCommand(string text)
|
||||||
|
{
|
||||||
|
string[] tokens = text.Split((char[])null, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (tokens.Length < 3 || !string.Equals(tokens[0], "get", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
Debug.Log($"[DebugConsole] Unrecognized command: \"{text}\". Try: get <type> <name>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string nameToken = string.Join(" ", tokens, 2, tokens.Length - 2);
|
||||||
|
ExecuteGet(tokens[1], nameToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// "get <type> <name>": grants any DraftOption of the given type whose DisplayName
|
||||||
|
// matches <name>, via the local player's DebugCommandRelay. Reuses DraftOption's
|
||||||
|
// existing type-agnostic ServerApply dispatch instead of hardcoding a grant path
|
||||||
|
// per option type.
|
||||||
|
private void ExecuteGet(string typeToken, string nameToken)
|
||||||
|
{
|
||||||
|
if (!DraftTypeAliases.TryGetValue(typeToken, out var optionType))
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[DebugConsole] Unknown draft option type \"{typeToken}\". " +
|
||||||
|
$"Valid types: {string.Join(", ", DraftTypeAliases.Keys)}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pool = DraftPool.Instance;
|
||||||
|
if (pool == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[DebugConsole] No DraftPool in this scene.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string normalizedTarget = Normalize(nameToken);
|
||||||
|
for (int i = 0; i < pool.Count; i++)
|
||||||
|
{
|
||||||
|
var option = pool.Get(i);
|
||||||
|
if (option == null || !optionType.IsInstanceOfType(option)) continue;
|
||||||
|
if (Normalize(option.DisplayName) != normalizedTarget) continue;
|
||||||
|
|
||||||
|
var relay = DebugCommandRelay.Local;
|
||||||
|
if (relay == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[DebugConsole] No local DebugCommandRelay found — " +
|
||||||
|
"is it on the Player prefab?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
relay.DebugGrantDraftOptionRpc(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.LogWarning($"[DebugConsole] No {typeToken} option named \"{nameToken}\" found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisplayName is player-facing draft card text (its spacing/casing is chosen for
|
||||||
|
// presentation, not for typing), so command matching strips everything but letters
|
||||||
|
// and digits before comparing — "Slow Area", "slow_area", and "SlowArea" all match
|
||||||
|
// the same option.
|
||||||
|
private static string Normalize(string s)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(s)) return string.Empty;
|
||||||
|
var sb = new StringBuilder(s.Length);
|
||||||
|
foreach (char c in s)
|
||||||
|
if (char.IsLetterOrDigit(c)) sb.Append(char.ToLowerInvariant(c));
|
||||||
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue