Polymart is now Voxel Shop! We're upgrading many features of the site, and during this open beta you will experience occasional bugs. Learn more  
NDS-API icon

NDS-API v2.2.0

Async-first, Redis Sync, High Performance Economy Engine for Developers.








NoieDigitalSystem API for Minecraft


A Next-Generation Economy Protocol for Minecraft Server Ecosystems 


NDS-API 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


Async-First DesignAll database operations return CompletableFuture<T> for non-blocking I/O. Zero main-thread blocking.
Native Folia SupportAutomatic detection with RegionScheduler and GlobalRegionScheduler integration.
PostgreSQL JSONB BackendFlexible schema with optimistic locking (version column) for data integrity.
Redis Cross-Server SyncReal-time state synchronization via Pub/Sub channels for multi-server deployments.
BigDecimal PrecisionAll monetary operations use java.math.BigDecimal. No floating-point rounding errors.
Virtual Threads (Java 21)Leverages Project Loom’s virtual threads for efficient I/O-bound operations.
Shared ConfigurationSingle config.yml shared across NDS ecosystem plugins at /plugins/NoieDigitalSystem/.
Facade Pattern ArchitectureClean separation of concerns with manager facades delegating to specialized components.



System Requirements


Java2121+Virtual threads require Java 21+
ServerPaper 1.21.4Paper/Folia 1.21.4+Folia for multi-threaded region support
PostgreSQL1215+JSONB and optimistic locking support
Redis6.07.0+Optional; required for cross-server sync
Memory512MB1GB+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:


# 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)


repositories {
mavenCentral()
}

dependencies {
compileOnly("io.github.misty4119:noiedigitalsystem-api:2.0.0")
}

Gradle (Groovy DSL)


repositories {
mavenCentral()
}

dependencies {
compileOnly 'io.github.misty4119:noiedigitalsystem-api:2.0.0'
}

Maven


<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+)


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)


name: YourPlugin
version: 1.0.0
main: com.example.yourplugin.YourPlugin
api-version: '1.21'
depend: [NoieDigitalSystem-API]



API Usage


Obtaining the API Instance


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>.


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).


// ═══════════════════════════════════════════════════════════════════════════
// 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.


// ═══════════════════════════════════════════════════════════════════════════
// 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:


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).


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)voidRemoves player data from memory cache. Called automatically on player quit.
clearCache()voidClears all cached player data. Use with caution.

DigitalManager


Manages server-level digitals and global player digital definitions.


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)BigDecimalGets 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


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>.


// ╔═══════════════════════════════════════════════════════════════════════════╗
// ║ 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:


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:


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:


Bukkit/PaperVirtual Thread (async)Requires Bukkit.getScheduler().runTask()
FoliaVirtual Thread (async)Requires entity.getScheduler().run() or RegionScheduler



Data Model


Digital Types


Player DigitalPer-playerPlayer-specific currencies/variablesPlayer’s private bank balance
Global Player DigitalDefinition → All PlayersStandard currencies everyone hascoinsgemspoints
Server DigitalServer-wideWorld state, event countersworld_boss_hpserver_event_score

Database Schema


The NDS-API uses a JSONB-based schema in PostgreSQL:


-- Primary data table
CREATE TABLE nds_data (
uuid UUID NOT NULL,
key VARCHAR(255) NOT NULL,
value JSONB NOT NULL DEFAULT '{}',
version BIGINT NOT NULL DEFAULT 1,
last_updated TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (uuid, key)
);

-- Index for JSONB queries
CREATE INDEX idx_nds_data_value ON nds_data USING GIN (value);

-- Index for temporal queries
CREATE INDEX idx_nds_data_updated ON nds_data (last_updated);

JSONB Structure


Player Data Example:


{
"coins": 1500.50,
"gems": 42,
"stamina": 100,
"_limits": {
"coins": 1000000,
"stamina": 160
}
}

Server Data Example (uuid = ‘00000000-0000-0000-0000-000000000000’):


{
"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:


# ═══════════════════════════════════════════════════════════════════════════
# 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


IdentityPlayer/Server/System identity abstraction with UUID-based addressing
AssetCurrency/variable definitions with scopes (Player, Global, Server)
EventAppend-only event sourcing for immutable audit trails
TransactionAtomic operations with configurable consistency modes
ResultStandardized success/failure response envelope

Protocol Repository


For the full protocol specification, multi-language SDK documentation, and Protocol Buffers definitions:


github.com/Misty4119/nds-api




Migration from Vault


NDS-API includes a Vault compatibility layer for gradual migration.


Enabling Vault Compatibility


vault:
enabled: true
enabled_digitals:
- "coins"
default_currency: "coins"

Migration Strategy


  1. Install NDS-API alongside your existing Vault provider
  2. Enable Vault compatibility in NDS-API configuration
  3. Test thoroughly in a staging environment
  4. Migrate data using the built-in migration tools
  5. Remove legacy Vault provider once verification is complete



Troubleshooting


Common Issues


NoieDigitalSystem-API not found!Plugin not installed or load order issueEnsure JAR is in plugins/ and check paper-plugin.yml dependencies
Connection pool exhaustedToo many concurrent database operationsIncrease poolSize in config; check for unclosed connections
Redis sync not workingIncorrect Redis configuration or firewallVerify Redis connection; check redis.enabled: true
ClassCastException on API accessMultiple NDS versions loadedEnsure only one NDS JAR is in plugins/
Slow performanceBlocking main thread with .get()Never use .get() on main thread; use async callbacks

Debug Mode


Enable debug logging for detailed diagnostics:


debug:
enabled: true
log_level: "DEBUG"
log_sql: true



Support



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.






Owned
Ready to download
This resource is already attached to your account. You can download the latest file any time.