using System; using System.Collections.Generic; using UnityEngine; namespace Guildlib.Runtime { // ========================================================================= // ATTRIBUTES // Apply these to your DataEntry subclasses to configure Guildlib behaviour. // ========================================================================= /// /// 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")] /// [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class ShardTargetAttribute : Attribute { public string ShardName { get; } public ShardTargetAttribute(string shardName) => ShardName = shardName; } /// /// 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")] /// [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class GuildLabelAttribute : Attribute { public string Label { get; } public GuildLabelAttribute(string label) => Label = label; } /// /// OPTIONAL — a short description shown in the web editor under the type name. /// /// Usage: /// [GuildDescription("Defines stats for all enemy and player characters.")] /// [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class GuildDescriptionAttribute : Attribute { public string Description { get; } public GuildDescriptionAttribute(string desc) => Description = desc; } /// /// 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")] /// [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class GuildPlatformsAttribute : Attribute { public string[] Platforms { get; } public GuildPlatformsAttribute(params string[] platforms) => Platforms = platforms; } /// /// 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")] /// [AttributeUsage(AttributeTargets.Class, Inherited = false)] public sealed class GuildTagsAttribute : Attribute { public string[] Tags { get; } public GuildTagsAttribute(params string[] tags) => Tags = tags; } /// /// 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 /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public sealed class GuildExcludeAttribute : Attribute { } /// /// 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; /// [AttributeUsage(AttributeTargets.Field)] public sealed class GuildDisplayNameAttribute : Attribute { } /// /// 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; /// [AttributeUsage(AttributeTargets.Field)] public sealed class GuildRefAttribute : Attribute { public string TargetShard { get; } public GuildRefAttribute(string targetShard) => TargetShard = targetShard; } /// /// OPTIONAL on a field — hints the web editor to render a multiline /// textarea instead of a single-line input. /// /// Usage: /// [GuildMultiline] /// public string description; /// [AttributeUsage(AttributeTargets.Field)] public sealed class GuildMultilineAttribute : Attribute { } /// /// 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; /// [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. // ========================================================================= /// /// Central registry for all platform identifiers used in this project. /// Edit this list to match your shipping targets. /// public static class GuildPlatformRegistry { /// /// All valid platform names for this project. /// Add your platforms here — these appear as checkboxes in the web editor. /// public static readonly string[] All = new[] { "pc", "ps5", "ps4", "xbox_series", "xbox_one", "switch", "mobile_ios", "mobile_android", "vr", }; /// /// Convenience constant — use as a shorthand for "ships on every platform". /// public const string AllPlatforms = "all"; } /// /// 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. /// public static class GuildTagRegistry { /// /// All valid tag names for this project. /// Add your tags here — these appear as checkboxes in the web editor. /// 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. // ========================================================================= /// /// 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; /// } /// [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; /// /// 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. /// [HideInInspector] public string parentId = null; [HideInInspector] public string typeName; [HideInInspector] public string shardName; /// /// 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. /// [HideInInspector] public List platforms = new(); /// /// 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. /// [HideInInspector] public List 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); } } } }