NDS-API
Async-first, Redis Sync, High Performance Economy Engine for Developers.
Quick challenge
How far can you run before the mobs catch you?
Minecraft check
Confirm your run
Complete the quick check to get your code.
NDS-API
NoieDigitalSystem API for Minecraft
A Next-Generation Economy Protocol for Minecraft Server Ecosystems
modern replacement for Vault is a high-performance, async-first economy protocol designed as a modern replacement for Vault. Built with native Folia support, PostgreSQL JSONB persistence, Redis-based cross-server synchronization, and `BigDecimal` precision for all monetary operations.
---
Architecture Overview
``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ NDS-API Architecture │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Your Plugin │ │ Your Plugin │ │ Your Plugin │ │ │ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ │ │ │ └──────────────────────┼──────────────────────┘ │ │ ▼ │ │ ┌───────────────────────────────────────────────────────────────────────┐ │ │ │ NDS-API Plugin Layer │ │ │ │ ┌─────────────────────┐ ┌─────────────────────────────────────┐ │ │ │ │ │ NdsApiPlugin │───▶│ Shared Configuration Manager │ │ │ │ │ │ (Entry Point) │ │ (/plugins/NoieDigitalSystem/) │ │ │ │ │ └─────────────────────┘ └─────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌───────────────────────────────────────────────────────────────────────┐ │ │ │ Core Manager Layer │ │ │ │ ┌─────────────────────┐ ┌─────────────────────────────────────┐ │ │ │ │ │ PlayerDigitalManager│ │ DigitalManager (Server Digitals) │ │ │ │ │ │ (Player Currencies) │ │ + GlobalDigitalManager │ │ │ │ │ └──────────┬──────────┘ └──────────────────┬──────────────────┘ │ │ │ │ │ │ │ │ │ │ └───────────────┬───────────────────┘ │ │ │ │ ▼ │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ │ │ Repository Layer (Facade) │ │ │ │ │ │ • Cache Manager (In-Memory + Redis) │ │ │ │ │ │ • JSONB Parser (PostgreSQL Native) │ │ │ │ │ │ • Transaction Pipeline (Optimistic Locking) │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────┼──────────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ │ │ PostgreSQL │ │ Redis │ │ Virtual │ │ │ │ (JSONB) │ │ (Pub/Sub) │ │ Threads │ │ │ │ Primary Store │ │ Cross-Server │ │ (Java 21) │ │ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ```
---
Key Features
| Feature | Description | |---------|-------------| | Facade Pattern Architecture | All database operations return `CompletableFuture<T>` for non-blocking I/O. Zero main-thread blocking. | | Facade Pattern Architecture | Automatic detection with `RegionScheduler` and `GlobalRegionScheduler` integration. | | Facade Pattern Architecture | Flexible schema with optimistic locking (`version` column) for data integrity. | | Facade Pattern Architecture | Real-time state synchronization via Pub/Sub channels for multi-server deployments. | | Facade Pattern Architecture | All monetary operations use `java.math.BigDecimal`. No floating-point rounding errors. | | Facade Pattern Architecture | Leverages Project Loom's virtual threads for efficient I/O-bound operations. | | Facade Pattern Architecture | Single `config.yml` shared across NDS ecosystem plugins at `/plugins/NoieDigitalSystem/`. | | Facade Pattern Architecture | Clean separation of concerns with manager facades delegating to specialized components. |
---
System Requirements
| Component | Minimum Version | Recommended | Notes | |-----------|-----------------|-------------|-------| | Memory | 21 | 21+ | Virtual threads require Java 21+ | | Memory | Paper 1.21.4 | Paper/Folia 1.21.4+ | Folia for multi-threaded region support | | Memory | 12 | 15+ | JSONB and optimistic locking support | | Memory | 6.0 | 7.0+ | Optional; required for cross-server sync | | Memory | 512MB | 1GB+ | Varies with player count and cache size |
---
Installation
Step 1: Download
Download the latest `NoieDigitalSystem-API-x.x.x.jar` from GitHub Releases.
Step 2: Deploy
Place the JAR file in your server's `plugins/` directory.
Step 3: Configure
The plugin uses a shared configuration architecture. Configuration is stored at:
``` plugins/NoieDigitalSystem/config.yml ```
Minimal Required Configuration:
```yaml
PostgreSQL Connection (Required)
postgresql: host: "localhost" port: 5432 database: "minecraft" username: "postgres" password: "your_secure_password" poolSize: 50
Redis Connection (Optional - for cross-server sync)
redis: enabled: true host: "localhost" port: 6379 password: "" timeout: 3000 ```
Step 4: Verify
Restart the server and check for successful initialization in the console:
``` [NoieDigitalSystem-API] Detected server type: Folia [NoieDigitalSystem-API] Using shared configuration at: /plugins/NoieDigitalSystem/config.yml [NoieDigitalSystem-API] Digital Manager initialization completed. [NoieDigitalSystem-API] NDS API Plugin enabled successfully! ```
---
Developer Integration
Dependency Setup
Gradle (Kotlin DSL)
```kotlin repositories { mavenCentral() }
dependencies { compileOnly("io.github.misty4119:noiedigitalsystem-api:2.0.0") } ```
Gradle (Groovy DSL)
```groovy repositories { mavenCentral() }
dependencies { compileOnly 'io.github.misty4119:noiedigitalsystem-api:2.0.0' } ```
Maven
```xml <dependency> <groupId>io.github.misty4119</groupId> <artifactId>noiedigitalsystem-api</artifactId> <version>2.0.0</version> <scope>provided</scope> </dependency> ```
Plugin Configuration
paper-plugin.yml (Paper 1.19.4+)
```yaml name: YourPlugin version: '1.0.0' main: com.example.yourplugin.YourPlugin api-version: '1.21' dependencies: server: NoieDigitalSystem-API: load: BEFORE required: true ```
plugin.yml (Legacy)
```yaml name: YourPlugin version: 1.0.0 main: com.example.yourplugin.YourPlugin api-version: '1.21' depend: [NoieDigitalSystem-API] ```
---
API Usage
Obtaining the API Instance
```java import noie.linmimeng.noiedigitalsystem.api.plugin.NdsApiPlugin; import noie.linmimeng.noiedigitalsystem.manager.DigitalManager; import noie.linmimeng.noiedigitalsystem.manager.PlayerDigitalManager; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin;
public class YourPlugin extends JavaPlugin {
private PlayerDigitalManager playerDigitalManager; private DigitalManager digitalManager;
@Override public void onEnable() { // Obtain the NDS-API plugin instance Plugin ndsPlugin = getServer().getPluginManager().getPlugin("NoieDigitalSystem-API");
// Validate plugin availability and type if (ndsPlugin == null) { getLogger().severe("NoieDigitalSystem-API is not installed!"); getServer().getPluginManager().disablePlugin(this); return; }
if (!(ndsPlugin instanceof NdsApiPlugin)) { getLogger().severe("NoieDigitalSystem-API is not the expected type!"); getServer().getPluginManager().disablePlugin(this); return; }
// Cast and obtain managers NdsApiPlugin ndsApi = (NdsApiPlugin) ndsPlugin; this.playerDigitalManager = ndsApi.getPlayerDigitalManager(); this.digitalManager = ndsApi.getDigitalManager();
getLogger().info("Successfully integrated with NoieDigitalSystem-API v2.0"); } } ```
Player Digital Operations
All player operations are asynchronous and return `CompletableFuture<T>`.
```java import java.math.BigDecimal; import java.util.UUID; import java.util.concurrent.CompletableFuture;
// ═══════════════════════════════════════════════════════════════════════════ // GET BALANCE // ═══════════════════════════════════════════════════════════════════════════ CompletableFuture<BigDecimal> balanceFuture = playerDigitalManager.getDigital(playerUUID, "coins");
balanceFuture.thenAccept(balance -> { // Executes on async thread - DO NOT call Bukkit API directly here getLogger().info("Player balance: " + balance.toPlainString()); });
// ═══════════════════════════════════════════════════════════════════════════ // GIVE DIGITAL (Add to balance) // ═══════════════════════════════════════════════════════════════════════════ BigDecimal amountToGive = new BigDecimal("100.50");
playerDigitalManager.giveDigital(playerUUID, "coins", amountToGive) .thenRun(() -> { getLogger().info("Successfully credited " + amountToGive + " coins"); }) .exceptionally(ex -> { getLogger().severe("Failed to credit coins: " + ex.getMessage()); return null; });
// ═══════════════════════════════════════════════════════════════════════════ // TAKE DIGITAL (Subtract from balance) // Returns false if insufficient balance // ═══════════════════════════════════════════════════════════════════════════ BigDecimal amountToTake = new BigDecimal("50.00");
playerDigitalManager.takeDigital(playerUUID, "coins", amountToTake) .thenAccept(success -> { if (success) { getLogger().info("Successfully debited " + amountToTake + " coins"); } else { getLogger().warning("Insufficient balance for debit operation"); } });
// ═══════════════════════════════════════════════════════════════════════════ // SET DIGITAL (Absolute value assignment) // ═══════════════════════════════════════════════════════════════════════════ BigDecimal newBalance = new BigDecimal("1000.00");
playerDigitalManager.setDigital(playerUUID, "coins", newBalance) .thenRun(() -> { getLogger().info("Balance set to " + newBalance); });
// ═══════════════════════════════════════════════════════════════════════════ // GET ALL PLAYER DIGITALS // ═══════════════════════════════════════════════════════════════════════════ playerDigitalManager.getPlayerDigitals(playerUUID) .thenAccept(digitals -> { digitals.forEach((name, value) -> { getLogger().info(name + ": " + value); }); }); ```
Server Digital Operations
Server digitals are server-wide variables (e.g., world boss HP, event counters).
```java // ═══════════════════════════════════════════════════════════════════════════ // GET SERVER DIGITAL // Note: This is a synchronous operation that reads from cache // ═══════════════════════════════════════════════════════════════════════════ BigDecimal worldBossHp = digitalManager.getDigitalMap().get("world_boss_hp");
// ═══════════════════════════════════════════════════════════════════════════ // MODIFY SERVER DIGITAL (Async operations) // ═══════════════════════════════════════════════════════════════════════════ digitalManager.giveDigital("world_boss_hp", new BigDecimal("1000")) .thenRun(() -> getLogger().info("World boss HP increased"));
digitalManager.takeDigital("world_boss_hp", new BigDecimal("500")) .thenAccept(success -> { if (success) { getLogger().info("World boss HP decreased"); } });
digitalManager.setDigital("world_boss_hp", new BigDecimal("10000")) .thenRun(() -> getLogger().info("World boss HP reset")); ```
Global Player Digitals
Global digitals are currencies available to all players (e.g., "coins", "gems"). Once created, every player automatically has access to this digital type.
```java // ═══════════════════════════════════════════════════════════════════════════ // CREATE GLOBAL DIGITAL // Parameters: name, initialAmount, limit (-1 = no limit) // ═══════════════════════════════════════════════════════════════════════════ digitalManager.createGlobalDigital("gems", 0.0, -1.0) .thenRun(() -> { getLogger().info("Created global digital 'gems' - available to all players"); });
// ═══════════════════════════════════════════════════════════════════════════ // CHECK IF GLOBAL DIGITAL EXISTS // ═══════════════════════════════════════════════════════════════════════════ digitalManager.isGlobalDigitalExists("gems") .thenAccept(exists -> { if (exists) { getLogger().info("'gems' is a registered global digital"); } });
// ═══════════════════════════════════════════════════════════════════════════ // GET ALL GLOBAL DIGITALS // ═══════════════════════════════════════════════════════════════════════════ digitalManager.getGlobalDigitals() .thenAccept(globals -> { globals.forEach((name, defaultAmount) -> { getLogger().info("Global: " + name + " (default: " + defaultAmount + ")"); }); }); ```
Error Handling Patterns
All async operations should implement proper exception handling:
```java playerDigitalManager.getDigital(playerUUID, "coins") .thenAccept(balance -> { // Success path processBalance(balance); }) .exceptionally(ex -> { // Error path - database connection, timeout, etc. getLogger().severe("Database operation failed: " + ex.getMessage());
// Log full stack trace for debugging if (getConfig().getBoolean("debug", false)) { ex.printStackTrace(); }
// Return null for Void-returning futures return null; });
// ═══════════════════════════════════════════════════════════════════════════ // CHAINED OPERATIONS WITH ERROR PROPAGATION // ═══════════════════════════════════════════════════════════════════════════ playerDigitalManager.getDigital(playerUUID, "coins") .thenCompose(balance -> { if (balance.compareTo(new BigDecimal("100")) >= 0) { return playerDigitalManager.takeDigital(playerUUID, "coins", new BigDecimal("100")); } return CompletableFuture.completedFuture(false); }) .thenAccept(success -> { if (success) { // Proceed with purchase logic } }) .exceptionally(ex -> { getLogger().severe("Transaction failed: " + ex.getMessage()); return null; }); ```
---
Core API Reference
PlayerDigitalManager
Manages player-specific digitals (currencies, points, custom variables).
| Method Signature | Return Type | Description | |-----------------|-------------|-------------| | `getDigital(UUID playerUUID, String digitalName)` | `CompletableFuture<BigDecimal>` | Retrieves the player's balance for the specified digital. Returns `BigDecimal.ZERO` if not found. | | `setDigital(UUID playerUUID, String digitalName, BigDecimal amount)` | `CompletableFuture<Void>` | Sets the player's balance to an exact value. Auto-creates the digital if it doesn't exist. | | `giveDigital(UUID playerUUID, String digitalName, BigDecimal amount)` | `CompletableFuture<Void>` | Adds the specified amount to the player's balance. Respects configured limits. | | `takeDigital(UUID playerUUID, String digitalName, BigDecimal amount)` | `CompletableFuture<Boolean>` | Subtracts the amount from balance. Returns `false` if insufficient funds. Atomic operation. | | `getLimit(UUID playerUUID, String digitalName)` | `CompletableFuture<BigDecimal>` | Gets the maximum balance limit. Returns `null` if no limit is set. | | `setLimit(UUID playerUUID, String digitalName, BigDecimal limit)` | `CompletableFuture<Void>` | Sets the maximum balance limit. Pass `null` to remove the limit. | | `getPlayerDigitals(UUID playerUUID)` | `CompletableFuture<Map<String, Double>>` | Returns all digitals and their values for a player. | | `getPlayerDigitalLimits(UUID playerUUID)` | `CompletableFuture<Map<String, Double>>` | Returns all digitals and their limits for a player. | | `isDigitalExists(UUID playerUUID, String digitalName)` | `CompletableFuture<Boolean>` | Checks if the digital exists for the player (includes global digitals). | | `createDigital(UUID playerUUID, String digitalName, BigDecimal initialAmount, BigDecimal limit)` | `CompletableFuture<Void>` | Creates a player-specific digital (rarely needed; auto-created on first use). | | `removeDigital(UUID playerUUID, String digitalName)` | `CompletableFuture<Void>` | Removes a player-specific digital entry. | | `grantDigital(UUID playerUUID, String digitalName, BigDecimal amount)` | `CompletableFuture<Void>` | Grants a special digital to a player (creates if not exists). | | `revokeDigital(UUID playerUUID, String digitalName)` | `CompletableFuture<Void>` | Revokes/removes a digital from a player. | | `loadPlayerData(UUID playerUUID)` | `CompletableFuture<Void>` | Pre-loads player data into cache. Called automatically on player join. | | `unloadPlayerData(UUID playerUUID)` | `void` | Removes player data from memory cache. Called automatically on player quit. | | `clearCache()` | `void` | Clears all cached player data. Use with caution. |
DigitalManager
Manages global player digital definitions and global player digital definitions.
| Method Signature | Return Type | Description | |-----------------|-------------|-------------| | `getDigitalMap()` | `Map<String, BigDecimal>` | Returns all server digitals and their current values (synchronous cache read). | | `getDigitalLimitMap()` | `Map<String, BigDecimal>` | Returns all server digitals and their limits. | | `setDigital(String digitalName, BigDecimal amount)` | `CompletableFuture<Void>` | Sets a server digital to an exact value. | | `giveDigital(String digitalName, BigDecimal amount)` | `CompletableFuture<Void>` | Adds to a server digital's value. | | `takeDigital(String digitalName, BigDecimal amount)` | `CompletableFuture<Boolean>` | Subtracts from a server digital. Returns `false` if insufficient. | | `removeDigital(String digitalName)` | `CompletableFuture<Void>` | Removes a server digital entirely. | | `getLimit(String digitalName)` | `BigDecimal` | Gets the limit for a server digital (synchronous). | | `setLimit(String digitalName, BigDecimal limit)` | `CompletableFuture<Void>` | Sets the limit for a server digital. | | `isDigitalExists(String digitalName)` | `CompletableFuture<Boolean>` | Checks if a server digital exists. | | `renameDigital(String oldName, String newName)` | `CompletableFuture<Void>` | Renames a server digital. Updates cache and Redis. |
Global Digital Methods
| Method Signature | Return Type | Description | |-----------------|-------------|-------------| | `createGlobalDigital(String digitalName, double initialAmount, double limit)` | `CompletableFuture<Void>` | Creates a global digital available to all players. Pass `-1` for no limit. | | `isGlobalDigitalExists(String digitalName)` | `CompletableFuture<Boolean>` | Checks if a global digital definition exists. | | `removeGlobalDigital(String digitalName)` | `CompletableFuture<Void>` | Removes a global digital definition. Does not delete player data. | | `getGlobalDigitals()` | `CompletableFuture<Map<String, BigDecimal>>` | Returns all global digital definitions. | | `getGlobalDigitalAmount(String digitalName)` | `CompletableFuture<BigDecimal>` | Gets the default amount for a global digital. | | `getGlobalDigitalLimit(String digitalName)` | `CompletableFuture<BigDecimal>` | Gets the limit for a global digital. |
---
Thread Safety & Concurrency
Async-First Architecture
Critical Rule: All NDS-API methods that perform I/O operations are asynchronous and return `CompletableFuture<T>`.
```java // ╔═══════════════════════════════════════════════════════════════════════════╗ // ║ CORRECT: Non-blocking async pattern ║ // ╚═══════════════════════════════════════════════════════════════════════════╝ playerDigitalManager.getDigital(playerUUID, "coins") .thenAccept(balance -> { // This callback executes on a virtual thread (Java 21) // Safe for I/O operations, NOT safe for Bukkit API calls processBalanceAsync(balance); });
// ╔═══════════════════════════════════════════════════════════════════════════╗ // ║ INCORRECT: Blocking the main thread ║ // ╚═══════════════════════════════════════════════════════════════════════════╝ // ⚠️ NEVER DO THIS - Will cause server lag/freeze BigDecimal balance = playerDigitalManager.getDigital(playerUUID, "coins").get(); ```
Bukkit/Paper Thread Model
When you need to interact with the Bukkit API from an async callback, you must schedule the operation on the main thread:
```java playerDigitalManager.getDigital(playerUUID, "coins") .thenAccept(balance -> { // Schedule Bukkit API calls on the main thread Bukkit.getScheduler().runTask(plugin, () -> { Player player = Bukkit.getPlayer(playerUUID); if (player != null && player.isOnline()) { player.sendMessage("Your balance: " + balance.toPlainString()); } }); }); ```
Folia Region Scheduler
For Folia servers, use the entity/region scheduler instead of the global scheduler:
```java playerDigitalManager.getDigital(playerUUID, "coins") .thenAccept(balance -> { // For Folia: Use the entity's scheduler Player player = Bukkit.getPlayer(playerUUID); if (player != null && player.isOnline()) { player.getScheduler().run(plugin, scheduledTask -> { player.sendMessage("Your balance: " + balance.toPlainString()); }, null); } }); ```
Thread Model Summary:
| Environment | Callback Thread | Bukkit API Access | |-------------|-----------------|-------------------| | Bukkit/Paper | Virtual Thread (async) | Requires `Bukkit.getScheduler().runTask()` | | Folia | Virtual Thread (async) | Requires `entity.getScheduler().run()` or `RegionScheduler` |
---
Data Model
Digital Types
| Type | Scope | Use Case | Example | |------|-------|----------|---------| | Server Digital | Per-player | Player-specific currencies/variables | Player's private bank balance | | Server Digital | Definition → All Players | Standard currencies everyone has | `coins`, `gems`, `points` | | Server Digital | Server-wide | World state, event counters | `world_boss_hp`, `server_event_score` |
JSONB Structure
Player Data Example:
```json { "coins": 1500.50, "gems": 42, "stamina": 100, "_limits": { "coins": 1000000, "stamina": 160 } } ```
Server Data Example (uuid = '00000000-0000-0000-0000-000000000000'):
```json { "world_boss_hp": 50000, "event_score": 12500, "_global_digitals": { "coins": { "initial": 0, "limit": -1 }, "gems": { "initial": 0, "limit": 9999 } } } ```
---
Configuration Reference
Complete configuration with all available options:
```yaml
═══════════════════════════════════════════════════════════════════════════
PostgreSQL Configuration (Required)
═══════════════════════════════════════════════════════════════════════════
postgresql: host: "localhost" port: 5432 database: "minecraft" username: "postgres" password: "your_secure_password"
Connection pool settings
poolSize: 50 # Maximum connections in pool connectionTimeout: 30000 # Connection timeout (ms) idleTimeout: 600000 # Idle connection timeout (ms) maxLifetime: 1800000 # Maximum connection lifetime (ms)
═══════════════════════════════════════════════════════════════════════════
Redis Configuration (Optional - for cross-server sync)
═══════════════════════════════════════════════════════════════════════════
redis: enabled: true host: "localhost" port: 6379 password: "" timeout: 3000
Channel prefixes for Pub/Sub
channels: player: "nds:player:" server: "nds:server:" global: "nds:global:"
═══════════════════════════════════════════════════════════════════════════
Digital Definitions
═══════════════════════════════════════════════════════════════════════════
digitals: coins: display_name: "Coins" persist_on_zero: false # Delete entry when balance reaches 0
gems: display_name: "Gems" persist_on_zero: true # Keep entry even at 0 balance
═══════════════════════════════════════════════════════════════════════════
Vault Integration (Compatibility Layer)
═══════════════════════════════════════════════════════════════════════════
vault: enabled: true enabled_digitals: - "coins" default_currency: "coins" ```
---
Protocol Specification
NDS-API is built on a cross-platform protocol specification designed for multi-language SDK support.
Protocol Domains
| Domain | Description | |--------|-------------| | Result | Player/Server/System identity abstraction with UUID-based addressing | | Result | Currency/variable definitions with scopes (Player, Global, Server) | | Result | Append-only event sourcing for immutable audit trails | | Result | Atomic operations with configurable consistency modes | | Result | Standardized success/failure response envelope |
Protocol Repository
For the full protocol specification, multi-language SDK documentation, and Protocol Buffers definitions:
---
Migration from Vault
NDS-API includes a Vault compatibility layer for gradual migration.
Enabling Vault Compatibility
```yaml vault: enabled: true enabled_digitals: - "coins" default_currency: "coins" ```
Migration Strategy
1. Remove legacy Vault provider alongside your existing Vault provider 2. Remove legacy Vault provider in NDS-API configuration 3. Remove legacy Vault provider in a staging environment 4. Remove legacy Vault provider using the built-in migration tools 5. Remove legacy Vault provider once verification is complete
---
Troubleshooting
Common Issues
| Symptom | Cause | Solution | |---------|-------|----------| | `NoieDigitalSystem-API not found!` | Plugin not installed or load order issue | Ensure JAR is in `plugins/` and check `paper-plugin.yml` dependencies | | Connection pool exhausted | Too many concurrent database operations | Increase `poolSize` in config; check for unclosed connections | | Redis sync not working | Incorrect Redis configuration or firewall | Verify Redis connection; check `redis.enabled: true` | | `ClassCastException` on API access | Multiple NDS versions loaded | Ensure only one NDS JAR is in `plugins/` | | Slow performance | Blocking main thread with `.get()` | Never use `.get()` on main thread; use async callbacks |
Debug Mode
Enable debug logging for detailed diagnostics:
```yaml debug: enabled: true log_level: "DEBUG" log_sql: true ```
---
Support
- Documentation: Documentation: - Documentation: %%MD1%% - Documentation: %%MD2%%
---
License
``` Copyright 2024-2026 Noie Linmimeng
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ```
---
NoieDigitalSystem — Next-generation economy protocol for Minecraft.
*Designed for the future. Built for today.*