BusinessesCore
A full-featured, modular business simulation plugin for Hytale. Players create and run businesses, hire workers, fulfill customer orders, compete server-wide, and grow through an upgrade system. All through a native in-game UI.
Architecture
BusinessesCore is built natively on Hytale's ECS (Entity Component System). All UI screens use the native InteractiveCustomUIPage system. Business state is managed entirely server-side through typed managers, with no client-side trust.
Modules are separate JARs that register themselves against the core API at startup. The core never imports module code directly; modules implement the BusinessModule interface and are discovered at runtime.
Installation
Core plugin
- Drop BusinessesCore.jar into your server's plugins/ folder.
- Start the server once; config files are generated automatically in plugins/BusinessesCore/.
- Edit settings.json and lang.json as needed.
- Restart or reload.
Modules (add-ons)
- Download the module JAR from the plugin page.
- Drop the module JAR into the same plugins/ folder alongside the core.
- Start the server; the module registers itself and generates its own config files.
Generated files
Quick Start
As a server admin
- Install the plugin and start your server.
- Give yourself OP or the businesses.admin permission.
- Run /business create MyShop foodshop to create a test business.
- Run /business setmanager <businessId> <yourName> to assign yourself as manager.
- Run /business manage to set a warp point and core location.
- Toggle on-service from /business info.
- A second player can now run /business browse and place an order.
As a player
- Run /business browse to see open businesses.
- Warp to one with /business warp <id>.
- Run /order at the business location to place an order.
- Wait for a worker to accept — a progress bar shows in your HUD.
- Collect your items when complete and leave a review with /business review <id> <1-5> <comment>.
Commands
All commands use the /business root. Arguments in <angle brackets> are required; [square brackets] are optional.
Player commands
Available to any player on the server.
| Command | Description | Permission |
|---|---|---|
| /businesses | Open the player hub - Browse, Leaderboard, My Business, Order Nearby. | none |
| /business browse | Open the business browser UI - shows all businesses that are currently open. | none |
| /business warp <id> | Warp to a business location after a 3-second countdown. Moving cancels the warp. | none |
| /business leaderboard | Open the leaderboard UI showing businesses ranked by competition votes. | none |
| /business contact <id> | Send a message to on-service workers at a business. Subject to a configurable cooldown. | none |
| /business review <id> <1-5> <comment> | Leave a star rating for a business. Requires at least one completed order there. | none |
| /business manual [player|manager|admin] | Read the in-game manual. Auto-detects your role if no argument is given. | none |
| /order | Order from the nearest open business. | none |
| /order cancel | Cancel your pending order. In-progress orders cannot be cancelled by customers. | none |
Worker commands
Available to any player who is a member (worker or manager) of a business.
| Command | Description | Permission |
|---|---|---|
| /business info | Open your business dashboard. Shows stats, active orders, and the on/off-service toggle. | worker |
| /business workers | Open the workers panel for your business. | worker |
| /business orders | Open the orders panel — lists all pending/active orders you can accept. | worker |
| /business balance | Check your personal balance and your business's balance (business balance visible to managers only). | worker |
| /business quit | Leave your current business. Cancels any orders you were handling. | worker |
| /business reply | Reply to the last customer who contacted your business. Opens a preset quick-reply picker. | worker |
Manager commands
Available to the designated manager of a business (or OP players).
| Command | Description | Permission |
|---|---|---|
| /business hire <player> | Send a hire request to an online player. They must accept via the UI. | manager |
| /business fire <player> | Immediately remove a worker from the business. | manager |
| /business promote <player> | Promote a worker's level (Trainee → Skilled → Expert). Higher levels earn more wages. | manager |
| /business demote <player> | Demote a worker's level. | manager |
| /business upgrade | Open the upgrades UI to spend business balance on level-ups. | manager |
| /business manage | Open the manager control panel — set warp point, business core, view reviews, manage workers. | manager |
| /business warp | Warp to your own business (no ID needed when you're a member). | manager |
Admin commands
Require the businesses.admin permission or OP.
| Command | Description | Permission |
|---|---|---|
| /business create <name> <module> | Create a new business with the given name and module type (e.g. foodshop, toolrepair). Name: 3-32 chars, alphanumeric + space/hyphen/underscore. | businesses.admin |
| /business admin | Open the admin panel — view all businesses, manage or delete any of them. | businesses.admin |
| /business list | List all businesses in chat with their ID, module, and upgrade level. | businesses.admin |
| /business info <id> | View a specific business dashboard by ID. | businesses.admin |
| /business setmanager <id> <player> | Assign a player as manager of a specific business. | businesses.admin |
| /business removemanager <id> | Remove the current manager from a business. | businesses.admin |
| /business delete <id> | Permanently delete a business and all its data. | businesses.admin |
| /business modules | List all currently registered business modules. | businesses.admin |
| /business competition start | Manually start a competition cycle (only when AutoStart is false or outside a cycle). | businesses.admin |
| /business competition end | Manually end the active competition and distribute rewards. | businesses.admin |
| /business reload | Reload config and lang files without restarting the server. | businesses.admin |
| /money give <player> <amount> | Give coins to a player's personal balance. | businesses.admin |
| /money remove <player> <amount> | Remove coins from a player's personal balance. | businesses.admin |
| /money set <player> <amount> | Set a player's personal balance to an exact amount. | businesses.admin |
| /money check <player> | Check a player's personal balance. | businesses.admin |
| /money bizgive <bizId> <amount> | Give coins to a business's balance. | businesses.admin |
| /money bizremove <bizId> <amount> | Remove coins from a business's balance. | businesses.admin |
| /money bizset <bizId> <amount> | Set a business's balance to an exact amount. | businesses.admin |
| /money bizcheck <bizId> | Check a business's current balance. | businesses.admin |
Permissions
| Node | Default | Description |
|---|---|---|
| businesses.admin | op | Full admin access: all admin commands, creating businesses, and UI panels. Also implies create permission. |
| * (OP) | op only | Wildcard permission granted to OP players. Bypasses all role checks. |
Configuration — Economy
Located under the Economy block in settings.json.
| Key | Type | Default | Description |
|---|---|---|---|
| CurrencyName | String | coins | Display name for the server currency. Shown in all UIs and messages. |
| StartingBalance | Double | 100.0 | Starting balance given to new players on first join. |
| PayPerReceipt | Double | 10.0 | Coins earned per completed order receipt, before multipliers (role × level × upgrade). |
| BaseWagePerInterval | Double | 5.0 | Base wage paid to every on-service worker at each payment interval, regardless of receipts. Also multiplied by role, level, and upgrade multipliers. |
| PaymentIntervalSeconds | Integer | 600 | How often (in seconds) wages are distributed from business balance to workers. Default = 10 minutes. |
Configuration — Business
Located under the Business block in settings.json.
| Key | Type | Default | Description |
|---|---|---|---|
| DefaultWorkerLimit | Integer | 3 | Max workers per business at upgrade level 1. Upgrades can increase this. |
| WarpEnabled | Boolean | true | Whether businesses can set and use warp points at all. |
| PeriodicSaveSeconds | Integer | 300 | How often all data is auto-saved to disk. Default = 5 minutes. |
| CustomerProximityRadius | Double | 15.0 | Max blocks from the business core a customer must be to open the service menu. |
| WorkerProximityRadius | Double | 20.0 | Max blocks from the core a worker can wander before the scheduler takes them off service automatically. |
| WorkerWorkRadius | Double | 10.0 | Stricter radius — how close to the core a worker must be to accept orders. |
| WorkerProximityOffServiceMessage | Boolean | true | Whether to notify the worker in chat when they are auto-removed from service due to proximity. |
| WorkerTaskMoveLimit | Double | 3.0 | Max blocks a worker can move from their task start position during an active order. Exceeding this teleports them back. |
Configuration — Anti-Exploit
Located under the AntiExploit block in settings.json.
| Key | Type | Default | Description |
|---|---|---|---|
| OrderCooldownSeconds | Integer | 60 | Minimum seconds between consecutive orders from the same customer at the same business. |
| MaxOrdersPerCustomerPerBusiness | Integer | 1 | Max concurrent pending/active orders a customer can have at a single business. |
| PerWorkerCustomerCooldownSeconds | Integer | 300 | A worker cannot work for the same customer again within this many seconds after completing an order. Prevents worker-customer farming loops. |
| EnableIpCheck | Boolean | false | Block orders between players sharing the same IP address. Useful against alt-account farming. |
Configuration — Competition
Located under the Competition block in settings.json.
| Key | Type | Default | Description |
|---|---|---|---|
| Enabled | Boolean | true | Whether the competition system is active at all. |
| AutoStart | Boolean | true | When true, a new competition starts automatically on a fixed cycle. When false, admins manage the cycle manually with /business competition start/end. |
| Name | String | Weekly Competition | Display name shown in the leaderboard UI subtitle. |
| DurationSeconds | Integer | 604800 | Length of each competition cycle in seconds. Default = 7 days. |
| TopRewardCount | Integer | 3 | How many top-ranked businesses receive monetary rewards at the end. |
| FirstPlaceReward | Double | 5000.0 | Business balance reward for 1st place. |
| SecondPlaceReward | Double | 2500.0 | Business balance reward for 2nd place. |
| ThirdPlaceReward | Double | 1000.0 | Business balance reward for 3rd place. |
Configuration — Database
Located under the Database block in settings.json.
| Key | Type | Default | Description |
|---|---|---|---|
| Type | String | LOCAL | Storage backend. LOCAL = JSON files (recommended). MYSQL and MONGODB are reserved for future implementation. |
| Host | String | localhost | Database host (unused with LOCAL). |
| Port | String | 3306 | Database port (unused with LOCAL). |
| Database | String | hytale | Database name (unused with LOCAL). |
| User | String | (empty) | Database username (unused with LOCAL). |
| Password | String | (empty) | Database password (unused with LOCAL). |
Language Keys — Core
All messages are in lang.json. Values support MiniMessage formatting. The placeholder {prefix} is replaced by the configured prefix (default: <dark_gray>[Businesses]</dark_gray>).
General
| Key | Default value |
|---|---|
| NoPermission | {prefix}<red>You don't have permission to do that.</red> |
| InvalidAmount | {prefix}<red>Invalid amount.</red> |
| NotInBusiness | {prefix}<red>You are not part of any business...</red> |
| BusinessNotFound | {prefix}<red>Business not found.</red> |
| AlreadyInBusiness | {prefix}<red>You are already part of a business.</red> |
| UnknownModule | Placeholder: {module} |
| BusinessDeleted | Placeholder: {business} |
Business creation & management
| Key | Placeholders |
|---|---|
| BusinessCreated | {business}, {id} |
| UpgradeSuccess | {business}, {level} |
| UpgradeFailed | {cost}, {balance} |
| AlreadyMaxLevel | {business} |
| BalanceBusiness | {business}, {balance} |
| BalancePersonal | {balance} |
| QuitBusiness | {business} |
| CoreSet | {x}, {y}, {z} |
| WarpPointSet | {x}, {y}, {z} |
Workers
| Key | Placeholders |
|---|---|
| WorkerSlotsFull | {current}, {max} |
| HireRequestSent | {player} |
| HireRequestNotify | {manager}, {business} |
| HireAccepted | {business} |
| HireAcceptNotify | {player}, {business} |
| HireDeclined | {business} |
| HireDeclineNotify | {player} |
| WorkerFired | {player}, {business} |
| YouWereFired | {business} |
| WorkerPromoted | {player} |
| YouWerePromoted | {business} |
| WorkerDemoted | {player} |
| YouWereDemoted | {business} |
| WorkerMovedDuringTask | (none) — shown when teleported back |
| WorkerTooFar | (none) — auto off-service proximity |
| WorkerTooFarToWork | (none) — too far to accept orders |
| WorkerCustomerCooldown | (none) |
Orders
| Key | Placeholders |
|---|---|
| OrderAcceptedCustomer | (none) |
| OrderNewWorkerNotify | {service} |
| OrderComplete | (none) |
| OrderCancelledRefund | {reason}, {amount} |
| OrderCancelledByYou | (none) |
| OrderDeliveryFailed | (none) — inventory was full |
| NoActiveOrders | (none) |
| YourOrdersEmpty | (none) |
Warp
| Key | Placeholders |
|---|---|
| WarpStarting | {business} |
| WarpCancelled | (none) |
| WarpSuccess | {business} |
| WarpUnavailable | (none) |
| WarpWorldNotFound | (none) |
Competition
| Key | Placeholders |
|---|---|
| CompBroadcastStart | (none) — server-wide broadcast |
| CompBroadcastEnd | (none) |
| CompBroadcastWinner | {business}, {rank}, {reward} |
| CompetitionActive | (none) |
| CompetitionInactive | (none) |
| CompetitionYourVote | {business} |
| VoteSuccess | {business} |
| VoteFailed | (none) |
| CannotVoteOwnBusiness | (none) |
Reviews
| Key | Placeholders |
|---|---|
| ReviewSubmitted | {business} |
| ReviewAlreadyReviewed | (none) |
| ReviewNoCompletedOrder | (none) |
| ReviewInvalidStars | (none) |
| CannotReviewOwnBusiness | (none) |
Messaging
| Key | Placeholders |
|---|---|
| WorkerMsgIncoming | {business}, {sender}, {message} |
| WorkerMsgSent | {business}, {count} |
| WorkerReplyIncoming | {business}, {worker}, {message} |
| NoContactToReply | (none) |
| ContactSent | {count}, {business} |
| ContactCooldown | {seconds} |
Order Lifecycle
Every order moves through a defined set of statuses managed by OrderManager.
Worker System
Roles
Workers have a role (Worker / Manager) and a level (Trainee / Skilled / Expert). Both affect wage multipliers.
- Only one manager per business. The manager is set by an admin with /business setmanager.
- Promote/demote level with /business promote and /business demote.
- Higher levels = higher wageMultiplier and speedMultiplier.
On/Off service
Workers toggle their service status from the business dashboard (/business info). Only on-service workers receive order notifications and can accept orders. Off-service workers are invisible to the order system.
Movement enforcement
When a worker accepts an order their position is recorded. Every 2 seconds, OrderManager checks if they have moved more than WorkerTaskMoveLimit blocks from that position. If so, they are teleported back and notified — the order is not cancelled.
A broader proximity check runs every 10 seconds. Workers farther than WorkerProximityRadius from the business core are automatically taken off service.
Wages
Every PaymentIntervalSeconds, the payment cycle runs:
- Each worker's receipts (completed orders) count is read.
- Wage = (receipts × PayPerReceipt + BaseWagePerInterval) × roleMultiplier × levelMultiplier × upgradeWageMultiplier
- Wage is deposited to the worker's personal balance and deducted from the business balance.
- Receipt count is reset to 0.
Economy System
BusinessesCore ships with a built-in economy (BuiltInEconomy). Balances are stored per-player UUID and persisted to disk.
- Player balances are separate from business balances.
- Customers pay from their personal balance into the business balance when an order is confirmed.
- Workers receive wages from the business balance into their personal balance.
- Refunds go back to the customer's personal balance. No money is created or destroyed on refund.
Upgrade System
Businesses start at level 1 and can be upgraded by spending business balance. Each upgrade level provides multipliers that affect worker wages, order speed, and pricing.
- Upgrade costs, worker slot increases, and multipliers are all configured in the upgrades section of settings.json.
- Managers access the upgrade screen via /business upgrade.
- The business must have sufficient balance — the cost is deducted automatically.
- UpgradeManager.getWorkSpeedMultiplier(moduleId, level) is applied to task duration at order confirmation time.
- UpgradeManager.getPriceMultiplier(moduleId, level) adjusts the final price shown to customers.
Competition System
Server-wide vote competitions where players vote for their favourite businesses. At the end of each cycle, the top-ranked businesses receive a balance reward.
- Players vote with /business vote <id>. One vote per player. Changing your vote removes it from the previous business.
- Workers cannot vote for their own business.
- Rankings are visible in /business leaderboard.
- With AutoStart: true, a new cycle starts automatically when the previous one ends.
- Admins can manually start/end cycles regardless of AutoStart with /business competition start and /business competition end.
- Winners are announced server-wide via the CompBroadcastWinner message (one per reward rank).
Review System
Customers who have completed at least one order at a business can leave a star rating (1-5) with a comment.
- One review per customer per business. Cannot be changed after submission.
- Workers cannot review their own business.
- Average rating is calculated from all reviews and shown in the browse UI, dashboard, and business cards.
- Ratings display as 4.7/5 with trailing .0 omitted (e.g. 4/5).
Customer Messaging
Customers can send a message to a business's on-service workers without placing an order.
- Command: /business contact <id> — opens a text picker, sends selected message to all on-service workers.
- Subject to ContactCooldown configured in anti-exploit settings.
- The business dashboard shows an inquiry banner when a message is pending. Workers can reply via /business reply or the reply button on the dashboard.
- Replies use preset quick-response strings shown in a picker UI.
- Only the most recent inquiry is kept per business.
Pending Messages
Messages can be queued for offline players using BusinessesAPI.sendOrQueue(uuid, rawTinyMessage). If the player is online the message is delivered immediately; if offline it is stored and delivered on their next login. Modules can also use BusinessesAPI.queueMessage(uuid, raw) to force a message to appear specifically at login.
Anti-Exploit
Multiple layers protect against economy farming:
- Self-order block — workers cannot order from their own business.
- Order cooldown — minimum time between orders at the same business.
- Worker-customer cooldown — a worker cannot serve the same customer repeatedly.
- Max concurrent orders — limits how many orders a customer can have open at one business.
- IP check (optional) — blocks orders between players on the same IP.
- Movement enforcement — workers are teleported back if they leave their task position.
- Proximity enforcement — workers are auto-removed from service if they leave the business area.
Module — Food Shop
Adds a foodshop business type. Workers prepare food orders for customers. All menu items, prices, and item IDs are configurable. Customers can order multiple units of the same item in one order.
Order flow
- Customer opens /order at the food shop, selects a food item.
- If MaxQuantityPerOrder > 1, a quantity picker is shown (1 to max).
- Order is placed and all on-service workers are notified.
- A worker accepts from /business orders — cooking begins immediately.
- After the cooking duration (scaled by quantity × upgrade speed multiplier), the food is delivered to the customer's inventory.
Food Shop Settings
File: FoodShop/settings.json
Items are stored as a JSON array — add or remove entries freely. No item count field, no numbered slots. Admins can also supply their own custom Hytale item IDs.
| Key | Type | Default | Description |
|---|---|---|---|
| CookingTimeMultiplier | Double | 1.0 | Global multiplier on all cooking durations. 0.5 = double speed, 2.0 = half speed. |
| MaxQuantityPerOrder | Integer | 5 | Maximum units of one item a customer can order at once. |
| ITEMS ARRAY — each entry is an object with these fields | |||
| Id | String | Unique internal identifier. Also used as fallback ItemId if ItemId is omitted. | |
| DisplayName | String | Name shown in the order menu. | |
| Description | String | Description shown in the order menu. | |
| ItemId | String | Hytale item ID to give the customer (e.g. Food_Bread). | |
| Price | Double | Price per unit ordered. | |
| CookSeconds | Integer | Cooking time per unit in seconds (multiplied by CookingTimeMultiplier). | |
| MinLevel | Integer | 1 | Minimum business upgrade level to unlock. 1 = always available. |
| DEFAULT ITEMS (9 entries, 3 tiers) | |||
| Level 1 | Cooked Wildmeat & Grilled Fish — 5 coins, 20 s | ||
| Level 1 | Fresh Bread & Popcorn — 7 coins, 25 s | ||
| Level 2 | Fruit Kebab & Mushroom Kebab — 14 coins, 45 s | ||
| Level 3 | Apple Pie, Meat Pie & Caesar Salad — 22 coins, 70 s | ||
Food Shop Language Keys
File: FoodShop/lang.json
| Key | Placeholders | Default description |
|---|---|---|
| OrderPlacedCustomer | (none) | Shown to customer after placing order. |
| PreparingWorker | {qty}, {food}, {duration} | Shown to worker when cooking starts. |
| PreparingCustomer | {qty}, {food}, {duration} | Shown to customer when cooking starts. |
| ReadyWorker | {qty}, {food} | Shown to worker when food is delivered. |
| CancelledWorker | (none) | Shown to worker when order is cancelled. |
| CancelledCustomer | (none) | Shown to customer when order is cancelled. |
Module — Tool Repair Shop
Adds a toolrepair business type. Customers drop damaged tools; workers repair them and return them fully restored. Three service tiers with configurable pricing per item.
Order flow
- Customer opens /order at the repair shop and selects a repair tier (Basic / Advanced / Instant).
- Order is created with isReadyForWorker = false — workers cannot see it yet.
- Customer is prompted to drop damaged tools from their hotbar. Each drop is intercepted — only items with maxDurability > 0 are accepted.
- After dropping items, the customer opens /order and clicks Confirm & Pay. This finalises the price and makes the order visible to workers.
- A worker accepts from /business orders. The customer is charged, the repair task begins.
- After the repair duration, items are returned to the customer at full durability.
Pricing formula
Tool Repair Settings
File: ToolRepairShop/settings.json
Service tiers are stored as a JSON array — add or remove tiers freely, no ServiceCount field needed.
| Key | Type | Default | Description |
|---|---|---|---|
| MaxItemsPerOrder | Integer | 10 | Max items a customer can drop for a single repair order. |
| SERVICES ARRAY — each entry is an object with these fields | |||
| Id | String | Unique internal identifier (e.g. basic_repair). | |
| DisplayName | String | Name shown in the order menu. | |
| Description | String | Description shown in the order menu. | |
| BasePrice | Double | Fixed base cost per order regardless of item count. | |
| PricePerItem | Double | Additional cost per item the customer drops. | |
| SecondsPerItem | Integer | Work duration per item in seconds. Total = itemCount × this. | |
| MinLevel | Integer | 1 | Minimum business upgrade level to unlock. 1 = always available. |
| DEFAULT TIERS (3 entries) | |||
| Basic Repair | 5 base + 10/item, 60 s/item, level 1 | ||
| Advanced Repair | 10 base + 15/item, 45 s/item, level 2 | ||
| Instant Repair | 25 base + 25/item, 10 s/item, level 3 | ||
Tool Repair Language Keys
File: ToolRepairShop/lang.json
| Key | Placeholders | Description |
|---|---|---|
| OrderPlaced | (none) | Shown to customer after choosing a tier — instructs them to drop items. |
| DropItemsPrompt | (none) | Shown again when the drop listener is activated. |
| ItemAdded | {item}, {qty}, {count}, {price} | Shown when an item is successfully dropped and added to the order. |
| MaxItemsReached | {max} | Shown when the customer tries to drop more than the max. |
| ItemNotDamageable | (none) | Shown when a dropped item has no durability (not a tool/weapon). |
| OrderAccepted | (none) | Shown to the worker when they accept a repair order. |
| RepairUnderway | (none) | Shown to customer when repair task starts. |
| TaskStarted | {count}, {duration} | Shown to worker when task begins. |
| TaskCompleteWorker | (none) | Shown to worker when repair is done. |
| TaskCancelledWorker | (none) | Shown to worker when order is cancelled. |
| ConfirmButtonLabel | (none) | Text shown on the "Confirm & Pay" button in the order UI. |
| OrderConfirmedRepair | {count}, {price} | Sent to on-service workers when a customer confirms their repair items. Includes item count and total price. |
Module API
To create a custom module, implement BusinessModule and register it with BusinessesAPI.registerModule() during plugin startup.
ModuleItemConfig — per-module config base class
All modules that expose a configurable list of items or service tiers should define a local ModuleItemConfig abstract class inside their own module package. This base provides the four fields common to every module: Id, DisplayName, Description, MinLevel.
Step 1 — copy this base class into your module's settings file:
Step 2 — declare the array field in the outer settings class:
The resulting settings.json is a clean, human-readable array:
ServiceDefinition
Database Providers
The storage layer is abstracted behind DatabaseProvider. Implement it to plug in any storage backend.
Register your provider in Main.initDatabase() by setting the DatabaseType to your type key and returning your implementation from the factory.
Custom Economy Provider
By default BusinessesCore uses its own built-in coin economy. You can replace it with any external economy system by implementing the EconomyProvider interface and registering it during your plugin's setup().
Register your provider from your economy plugin's setup() method:
The provider is also accessible to modules via BusinessesAPI:
HytaleBase
BusinessesCore and all its modules are built on top of HytaleBase, an open-source utility library for Hytale plugins. It provides EasyConfig, EasyConfigBuilder, TinyMessage, command abstractions, and other shared infrastructure.
HytaleBase is publicly available on GitHub:
When building a module, include HytaleBase as a dependency so your module has access to the same utilities (EasyConfig, ArrayCodec, TinyMessage, etc.) without duplicating code. All HytaleBase classes are provided by the server at runtime — do not shadow-jar them into your module.