Files
MAGI/unity/Assets/Guildlib/Runtime/DataEntry.cs
2026-03-16 21:38:49 +01:00

341 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
namespace Guildlib.Runtime
{
// =========================================================================
// ATTRIBUTES
// Apply these to your DataEntry subclasses to configure Guildlib behaviour.
// =========================================================================
/// <summary>
/// REQUIRED on every concrete DataEntry subclass.
/// Declares which shard database this type belongs to.
///
/// The shard name is free-form — use any string you want.
/// The server will auto-create a new .sqlite file for any new shard name
/// the first time an entry of that type is pushed.
///
/// Usage:
/// [ShardTarget("characters")]
/// [ShardTarget("ui")]
/// [ShardTarget("levels")]
///
/// Shard names are lowercase, no spaces. Use underscores if needed:
/// [ShardTarget("vfx_particles")]
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class ShardTargetAttribute : Attribute
{
public string ShardName { get; }
public ShardTargetAttribute(string shardName) => ShardName = shardName;
}
/// <summary>
/// OPTIONAL — gives the type a human-readable label for the web editor.
/// If omitted, the class name is used.
///
/// Usage:
/// [GuildLabel("Character Stats")]
/// [GuildLabel("UI Button")]
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class GuildLabelAttribute : Attribute
{
public string Label { get; }
public GuildLabelAttribute(string label) => Label = label;
}
/// <summary>
/// OPTIONAL — a short description shown in the web editor under the type name.
///
/// Usage:
/// [GuildDescription("Defines stats for all enemy and player characters.")]
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class GuildDescriptionAttribute : Attribute
{
public string Description { get; }
public GuildDescriptionAttribute(string desc) => Description = desc;
}
/// <summary>
/// OPTIONAL — declares which platforms this entry type is relevant for.
/// This is a DEFAULT for new entries of this type. Individual entries can
/// override their platform tags at runtime in the web editor.
///
/// Platforms are free-form strings — define your own set.
/// Common examples: "pc", "ps5", "xbox", "switch", "mobile", "all"
///
/// Usage:
/// [GuildPlatforms("all")]
/// [GuildPlatforms("pc", "ps5", "xbox")]
/// [GuildPlatforms("switch", "mobile")]
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class GuildPlatformsAttribute : Attribute
{
public string[] Platforms { get; }
public GuildPlatformsAttribute(params string[] platforms) => Platforms = platforms;
}
/// <summary>
/// OPTIONAL — declares which build configurations (tags) this entry type
/// belongs to by default.
///
/// Tags are free-form — define your own taxonomy.
/// Examples: "debug", "release", "demo", "dlc1", "alpha"
///
/// Usage:
/// [GuildTags("release", "demo")]
/// [GuildTags("debug")]
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class GuildTagsAttribute : Attribute
{
public string[] Tags { get; }
public GuildTagsAttribute(params string[] tags) => Tags = tags;
}
/// <summary>
/// OPTIONAL on a field or property — exclude it from schema export and sync.
/// Use for Unity-internal or editor-only fields that should not hit the server.
///
/// Usage:
/// [GuildExclude]
/// public Texture2D previewTexture; // Unity asset ref, not serialisable to DB
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class GuildExcludeAttribute : Attribute { }
/// <summary>
/// OPTIONAL on a field — marks it as the display name for this entry
/// in the web editor's entry list. Without this, entryId is shown.
///
/// Usage:
/// [GuildDisplayName]
/// public string characterName;
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public sealed class GuildDisplayNameAttribute : Attribute { }
/// <summary>
/// OPTIONAL on a field — marks it as a reference to another shard entry.
/// The web editor will show a picker instead of a plain text field.
///
/// Usage:
/// [GuildRef("characters")]
/// public string ownerCharacterId;
///
/// [GuildRef("audio")]
/// public string deathSoundId;
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public sealed class GuildRefAttribute : Attribute
{
public string TargetShard { get; }
public GuildRefAttribute(string targetShard) => TargetShard = targetShard;
}
/// <summary>
/// OPTIONAL on a field — hints the web editor to render a multiline
/// textarea instead of a single-line input.
///
/// Usage:
/// [GuildMultiline]
/// public string description;
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public sealed class GuildMultilineAttribute : Attribute { }
/// <summary>
/// OPTIONAL on a string field — constrains the value to one of the
/// provided options. The web editor renders a dropdown.
///
/// Usage:
/// [GuildEnum("None", "Rare", "Epic", "Legendary")]
/// public string rarity;
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public sealed class GuildEnumAttribute : Attribute
{
public string[] Options { get; }
public GuildEnumAttribute(params string[] options) => Options = options;
}
// =========================================================================
// PLATFORM / TAG REGISTRY
// Define your project's platform and tag vocabulary here.
// These are used for validation and for populating dropdowns in the
// web editor. Add or remove entries freely — they're just strings.
// =========================================================================
/// <summary>
/// Central registry for all platform identifiers used in this project.
/// Edit this list to match your shipping targets.
/// </summary>
public static class GuildPlatformRegistry
{
/// <summary>
/// All valid platform names for this project.
/// Add your platforms here — these appear as checkboxes in the web editor.
/// </summary>
public static readonly string[] All = new[]
{
"pc",
"ps5",
"ps4",
"xbox_series",
"xbox_one",
"switch",
"mobile_ios",
"mobile_android",
"vr",
};
/// <summary>
/// Convenience constant — use as a shorthand for "ships on every platform".
/// </summary>
public const string AllPlatforms = "all";
}
/// <summary>
/// Central registry for all tag identifiers used in this project.
/// Tags can represent build configs, DLC groups, content regions,
/// quality tiers, or any other grouping you need.
/// </summary>
public static class GuildTagRegistry
{
/// <summary>
/// All valid tag names for this project.
/// Add your tags here — these appear as checkboxes in the web editor.
/// </summary>
public static readonly string[] All = new[]
{
// Build types
"release",
"debug",
"demo",
// DLC / content waves
"base_game",
"dlc_1",
"dlc_2",
// Quality / LOD tiers
"high",
"medium",
"low",
// Region
"global",
"region_jp",
"region_eu",
"region_na",
};
}
// =========================================================================
// DataEntry BASE CLASS
// Every entry stored in Guildlib inherits from this.
// =========================================================================
/// <summary>
/// Base class for all Guildlib-managed data entries.
///
/// To create a new data type:
///
/// 1. Subclass DataEntry
/// 2. Add [ShardTarget("your_shard_name")] ← required
/// 3. Optionally add [GuildLabel], [GuildDescription], [GuildPlatforms], [GuildTags]
/// 4. Add your public fields
/// 5. Open Window → Guildlib → Sync Panel and click Upload Schema
///
/// Example:
///
/// [ShardTarget("characters")]
/// [GuildLabel("Character Stats")]
/// [GuildPlatforms("all")]
/// [GuildTags("base_game", "release")]
/// [CreateAssetMenu(menuName = "Guildlib/Character Stats")]
/// public class CharacterStats : DataEntry
/// {
/// [GuildDisplayName]
/// public string characterName;
/// public float maxHealth;
/// public float moveSpeed;
///
/// [GuildEnum("Warrior", "Mage", "Rogue")]
/// public string characterClass;
/// }
/// </summary>
[Serializable]
public abstract class DataEntry : ScriptableObject
{
// ── Guildlib-managed fields (not shown in web editor forms) ──────────
[HideInInspector]
public string entryId = Guid.NewGuid().ToString("N");
[HideInInspector]
public long rowVersion = 0;
/// <summary>
/// ID of a parent entry (same shard). Set this to link a child entry
/// to its parent, e.g. a specific SwordData instance linked to a
/// WeaponData template.
/// </summary>
[HideInInspector]
public string parentId = null;
[HideInInspector]
public string typeName;
[HideInInspector]
public string shardName;
/// <summary>
/// Platform tags for this specific entry instance.
/// Defaults to the class-level [GuildPlatforms] attribute value,
/// but can be overridden per-entry in the web editor.
/// </summary>
[HideInInspector]
public List<string> platforms = new();
/// <summary>
/// Content/build tags for this specific entry instance.
/// Defaults to the class-level [GuildTags] attribute value,
/// but can be overridden per-entry in the web editor.
/// </summary>
[HideInInspector]
public List<string> tags = new();
protected virtual void OnEnable()
{
typeName = GetType().FullName;
var shard = GetType().GetCustomAttributes(typeof(ShardTargetAttribute), false);
shardName = shard.Length > 0
? ((ShardTargetAttribute)shard[0]).ShardName
: "default";
// Apply class-level platform defaults if none set yet
if (platforms.Count == 0)
{
var pa = (GuildPlatformsAttribute[])GetType()
.GetCustomAttributes(typeof(GuildPlatformsAttribute), false);
if (pa.Length > 0) platforms.AddRange(pa[0].Platforms);
else platforms.Add(GuildPlatformRegistry.AllPlatforms);
}
// Apply class-level tag defaults if none set yet
if (tags.Count == 0)
{
var ta = (GuildTagsAttribute[])GetType()
.GetCustomAttributes(typeof(GuildTagsAttribute), false);
if (ta.Length > 0) tags.AddRange(ta[0].Tags);
}
}
}
}