Cassaforte

Cassaforte

An Hytale API to easily integrate other plugins economies, chats and permissions

307.0 загрузок
Обновлён 16 дней назад

Описание

Кассафорте

Обзор

Cassaforte предлагает чистый слой абстракции для плагинов Hytale, вдохновленный VaultAPI для Bukkit. Он позволяет плагинам взаимодействовать с системами экономии, разрешения и чата через единый интерфейс, независимо от базовой реализации.

Плагины с использованием Cassaforte

Особенности

  • Реестр услуг: Централизованная регистрация и поиск реализации услуг
  • Экономика APIУправление балансом, депозиты, снятие средств и банковская поддержка
  • Разрешение APIУправление разрешениями игроков и групп
  • Chat API: форматирование префикса / суффикса и информационные узлы игрока / группы
  • Идентификация на основе UUIDПолная поддержка оффлайн игроков
  • БезопасныйАтомные операции для надежного параллельного доступа
  • Нулевая зависимостьЧистый Java без зависимостей времени выполнения
  • Политика первых побед: Успешна только первая регистрация, предотвращая случайные перезаписи.

установка

Добавьте Cassaforte в свой проект Gradle:

ЗависимостьРезолюция Управление {
RepositoriesMode.set (RepositoriesMode.FAIL_ON_PROJECT_REPOS)
репозитории {
mavenCentral()
{url }https://jitpack.io " ?
?
?

зависимости {
compileOnly ("it.cassaforte:api:v0.1.2")
?

Архитектура

Кассафорте использует Структура реестра услуг где плагины регистрируются сами, а потребительские плагины получают эти реализации прозрачно.

???????????????????????????????????
│ Cassaforte API │
│ │
?????????????????????????????
│ │ Экономика │ │ Разрешение │ │ Чат │ │
│ │ Интерфейс │ │ Интерфейс │ │
???????????????????????????
│ │ │ │
│ ??????????????????????????????
│ │ │
????????????????????????
│ │ Кассафорте │ │
│ │ Реестр услуг │ │
??????????????????????
????????????????????????????????????
│
???????????????????????????????
│ │
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
│ Экономический плагин │ │ Потребительский плагин │
│ (Registers impl) │ │ (Retrieves impl) │
?????????????????????????????????

использование

Для исполнителей услуг

Если вы создаете экономию, разрешение или плагин чата, реализуйте интерфейс и зарегистрируйте его в Cassaforte:

импортировать com.hypixel.hytale.server.core.plugin.JavaPlugin;
импорт com.hypixel.hytale.server.core.plugin.JavaPluginInit;
импортировать it.cassaforte.api.Cassaforte;
импортировать it.cassaforte.api.economy.AbstractEconomy;
импортировать it.cassaforte.api.economy.EconomyResponse;
Импорт javax.annotation.Nonnull
Импорт java.util. UUID;

Общественный класс MyEconomyPlugin расширяется Абстрактная экономика {
частный статический экземпляр MyEconomyPlugin;

Публичный MyEconomyPlugin(@Nonnull JavaPluginInit init)
Super(init);
Пример = это;
?

Публичный статический MyEconomyPlugin get() {
возвратный экземпляр;
?

@Override
Защищенная установка void() {
// Зарегистрируйте эту реализацию экономики в Кассафорте
Boolean registered = Cassaforte.registerEconomy (это);
Если (!зарегистрировано)
GetLogger(.warning()Другая экономика уже зарегистрирована!);
вернуться;
?
получитьLogger(.info)("Экономика успешно зарегистрирована!");
?

@Override
Защищенное отключение void shutdown() {
// Очистка ресурсов при необходимости
?

Реализация необходимых экономических методов
@Override
Public EconomyRespons DepositPlayer (UUID плеер) Id, двойная сумма {
// Ваша реализация
Возвратить новый EconomyResponse (количество, новыйбаланс, EconomyResponse.ResponseType.SUCCESS);
?

@Override
Публичный двойной GetBalance (UUID плеер) Id) {
// Ваша реализация
возврат 0,0;
?

... реализуют другие необходимые методы
?

Для потребителей услуг

Если вы используете экономию, разрешение или службу чата в своем плагине, верните зарегистрированную реализацию через Cassaforte:

импортировать com.hypixel.hytale.server.core.plugin.JavaPlugin;
импорт com.hypixel.hytale.server.core.plugin.JavaPluginInit;
импортировать it.cassaforte.api.Cassaforte;
импортировать it.cassaforte.api.economy.Economy;
импортировать it.cassaforte.api.permission.
Импортировать it.cassaforte.api.chat. Чат.
Импорт javax.annotation.Nonnull

Публичный класс MyPlugin расширяет JavaPlugin {
частная экономика;
частное разрешение;
частный Чат-чат;

Публичный MyPlugin(@Nonnull JavaPluginInit init)
Super(init);
?

@Override
Защищенная установка void() {
// Восстановление зарегистрированных услуг
экономика = Cassaforte.getEconomy();
Разрешение = Cassaforte.getPermission();
чат = Cassaforte.getChat();

// Проверьте, доступны ли услуги
Если (экономика! = нуль & & экономика.isEnabled())
getLogger(.info)("Экономическая услуга доступна:" + economy.getName());
Другое дело {
GetLogger().warning("Нет доступных экономических услуг")
?

Если (разрешение!) - не имеет права и разрешения. Включено() {
getLogger(.info)("Доступная услуга:" + permission.getName());
Другое дело {
GetLogger(.warning("No permission service available")
?

Если (chat!=null)
ПолучитьLogger(.info)("Чат сервис доступен");
?
?

Акция GiveMoney (UUID Player) Id, двойная сумма {
Если (экономика! = нуль & & экономика.isEnabled())
var response = economy.depositPlayer (playerId, сумма);
Если (response.transactionSuccess()) {
getLogger(.info)("Депозитная" + сумма + "игроку");
?
Другое дело {
GetLogger(.warning()Не вносить депозит: Экономика недоступна";
?
?

Public Boolean CheckPermission (UUID плеер) Id, струнный узел
Если (разрешение!) - не имеет права и разрешения. Включено() {
Разрешение на возвращение. playerHas (playerId, node);
?
Возврат ложных
?

Публичный формат струн PlayerName (UUID плеер) Id, String PlayerName
Если (chat!=null)
String prefix = chat.getPlayerPrefix (playerId, "");
Струнный суффикс = chat.getPlayerSuffix (playerId, "");
возврат префикса + PlayerName + суффикс;
?
Возвратный игрок Имя;
?
?

Использование Hytale Plugin Manager

Вы также можете получить Cassaforte напрямую через менеджер плагинов Hytale:

Импорт com.hypixel.hytale.server.core.plugin.PluginManager
импортировать com.hypixel.hytale.common.plugin.PluginIdentifier;
импортировать it.cassaforte.api.Cassaforte;

Публичный класс MyPlugin расширяет JavaPlugin {

@Override
Защищенная установка void() {
Способ 1: Прямой статический доступ (рекомендуется)
Экономика = Cassaforte.getEconomy();
Если (экономика! = нуль)
getLogger(.info)("Экономика:" + economy.getName());
?

// Способ 2: Доступ через PluginManager для получения экземпляра плагина
PluginManager pm = PluginManager.get();
Плагинидентификатор cassaforte Id = новый PluginIdentifier («it.cassaforte», «Cassaforte»);

Если (pm.hasPlugin (cassaforteId))
getLogger(.info)("Загружен плагин Кассафорте");
// Доступ к услугам осуществляется с помощью статических методов Кассафорте.
Экономика2 = Cassaforte.getEconomy();
Разрешение = Cassaforte.getPermission();
Чат-чат = Cassaforte.getChat();
?
?
?

API Ссылка

Реестр Кассафорте

The it.cassaforte.api.Cassaforte Класс предоставляет статические методы регистрации и поиска услуг.

Методы регистрации

Регистрация внедрения экономики (возвращается ложным, если уже зарегистрировано)
Булев успех = Cassaforte.registerЭкономика (экономика);

// Регистрация реализации разрешения
Boolean success = Cassaforte.registerPermission (разрешение на разрешение);

// Регистрация реализации чата
Булев успех = Cassaforte.register Чат (Chat Chat)

Методы поиска

// Зарегистрировать экономику (ноль, если никто не зарегистрирован)
Экономика = Cassaforte.getEconomy();

// Получить зарегистрированное разрешение (ноль, если не зарегистрировано)
Разрешение = Cassaforte.getPermission();

// Зарегистрируйтесь в чате (ноль, если никто не зарегистрирован)
Чат-чат = Cassaforte.get Чат();

Экономика API

импортировать it.cassaforte.api.economy.Economy;
импортировать it.cassaforte.api.economy.EconomyResponse;
Импорт java.util. UUID;

// Проверьте, есть ли у игрока аккаунт
boolean hasAccount = economy.hasAccount (playerId);

// Баланс игрока
Двойной баланс = economy.getBalance (playerId);

// Депозитные деньги
EconomyResponse response = economy.depositPlayer (playerId, 100.0);
Если (response.transactionSuccess()) {
Успех - новый баланс: ответ. баланс
?

// Снять деньги
EconomyResponse response = economy.withdrawPlayer (playerId, 50.0);

// Проверьте, может ли игрок позволить себе
boolean canAfford = economy.has (playerId, 75.0);

Банковские операции (при их поддержке)
Если (economy.hasBankSupport()) {
economy.createBank("MyBank", playerId);
Двойной банкБаланс = economy.bankBalance («МойБанк»)
?

Формат валюты
Формат строки = economy.format(1000.50); // "$1000.50"

Разрешение API

импортировать it.cassaforte.api.permission.
Импорт java.util. UUID;

// Разрешения игроков
булевый имеет Пермь = permission.player Has (игрок) Id, "essentials.tp";
permission.playerAdd (играющий) Id, "essentials.tp";
permission.playerУдалить(удалить) Id, "essentials.tp";

// Групповые разрешения
булевая группа Has = permission.groupHas("admin", "essentials.*");
permission.groupAdd("admin", "essentials.*");
permission.groupRemove("admin", "essentials.*");

// Членство в группе
Boolean inGroup = permission.playerInGroup Id, "vip";
permission.playerAddGroup (игрок) Id, "vip";
permission.playerRemoveGroup (игрок) Id, "vip";

// Получить группы игроков
List<String> groups = permission.getPlayerGroups (игрок) Id);
String PrimaryGroup = permission.getPrimaryGroup Id);

Chat API

Импортировать it.cassaforte.api.chat. Чат.
Импорт java.util. UUID;

// Префикс/суффикс игрока
String prefix = chat.getPlayerPrefix (playerId, "");
Струнный суффикс = chat.getPlayerSuffix Ид, "";
chat.setPlayerPrefix (игрок) Ид, «[Админ]»;
Chat.setPlayerSuffix (игрок) Id, "*";

// Групповой префикс/суффикс
Струнная группаPrefix = chat.getGroupPrefix («vip», «»);
chat.setGroupPrefix("vip", "[VIP]);

// Узлы информации (хранилище метаданных)
chat.setPlayerInfoString (играющий) Id, "nickname", "Player1";
Прозвище: chat.getPlayerInfoString (игрок) Id, "никнейм", "";

chat.setPlayerInfoInteger(игрок) Id, "kills", 100;
int kills = chat.getPlayerInfoInteger Id, "kills", 0;

Структура проекта

it.cassaforte.api/
Недоброжелательность - Кассафорте. ява Центральный реестр услуг
─ Экономика ─ Экономика
↑ Экономика. — Экономика. Java # Основной интерфейс экономики
│ │ │ ─ ─ AbstractEconomy.java # Реализация базы с дефолтами
ЭкономикаОтвет. java # Operation Response Обертка
─ ─ разрешение/
- Разрешение. java #Разрешение и управление группой
── чат/
Чат.java #Чат форматирование и метаданные

Безопасность ниток

Все Кассафорте Методы реестра являются потоково-безопасными. Регистрация использует атомные операции сравнения и установки, чтобы обеспечить успех только первой регистрации. Операции по поиску не блокируются и безопасны для вызова из любой нити.

Лучшие практики

  1. Регистрируйтесь рано: Всегда регистрируйте свои услуги в установка() или Старт() Чтобы убедиться, что он доступен, прежде чем другие плагины нуждаются в нем.
  2. Handle Null возвращается: Всегда проверяйте недействительность при получении услуг - они не могут быть зарегистрированы
  3. Проверка включена()Проверить Включено() перед использованием экономических или разрешительных услуг
  4. Используйте статические аксессуарыПредоставить статический GetInstance() Способ в вашем плагине для легкого доступа к другим плагинам
  5. Благодатная деградация: Ваш плагин должен работать, даже если дополнительные услуги недоступны.

Здание

градостроительный

Строение производит:

  • банку Стандартный JAR с зависимостями
  • тени Джар Жирный JAR с затененными зависимостями (Gson Relocated)

Требования

  • Java 21+
  • Серия 9.0+
  • Hytale Server API (только для компиляций)

Лицензия

МТИ

Вклад

Взносы приветствуются! Пожалуйста, не стесняйтесь отправлять запрос Pull.

Вдохновленный

Этот проект вдохновлен VaultAPI Bukkit/Minecraft, адаптированный для платформы Hytale.

Показать оригинальное описание (English)

Cassaforte

Overview

Cassaforte offers a clean abstraction layer for Hytale plugins, inspired by VaultAPI for Bukkit. It allows plugins to interact with economy, permission, and chat systems through a unified interface, regardless of the underlying implementation.

Plugins using Cassaforte

Features

  • Service Registry: Centralized registration and retrieval of service implementations
  • Economy API: Balance management, deposits, withdrawals, and bank support
  • Permission API: Player and group permission management
  • Chat API: Prefix/suffix formatting and player/group info nodes
  • UUID-based identification: Full offline player support
  • Thread-safe: Atomic operations for reliable concurrent access
  • Zero dependencies: Pure Java with no runtime dependencies
  • First-wins policy: Only first registration succeeds, preventing accidental overwrites

Installation

Add Cassaforte as a dependency in your Gradle project:

dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            mavenCentral()
            maven { url 'https://jitpack.io' }
        }
    }

dependencies {
    compileOnly("it.cassaforte:api:v0.1.2")
}

Architecture

Cassaforte uses a service registry pattern where plugin implementations register themselves, and consumer plugins retrieve these implementations transparently.

┌─────────────────────────────────────────────────────────────┐
│                      Cassaforte API                         │
│                                                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │   Economy    │  │  Permission  │  │     Chat     │      │
│  │   Interface  │  │   Interface  │  │   Interface  │      │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘      │
│         │                 │                 │                │
│         └─────────────────┴─────────────────┘                │
│                           │                                  │
│                   ┌───────┴────────┐                         │
│                   │ Cassaforte     │                         │
│                   │ Service Registry│                        │
│                   └────────────────┘                         │
└─────────────────────────────────────────────────────────────┘
                            │
          ┌─────────────────┴─────────────────┐
          │                                   │
┌─────────────────────┐           ┌─────────────────────┐
│  Economy Plugin     │           │  Consumer Plugin    │
│  (Registers impl)   │           │  (Retrieves impl)   │
└─────────────────────┘           └─────────────────────┘

Usage

For Service Implementers

If you're creating an economy, permission, or chat plugin, implement the interface and register it with Cassaforte:

import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import it.cassaforte.api.Cassaforte;
import it.cassaforte.api.economy.AbstractEconomy;
import it.cassaforte.api.economy.EconomyResponse;
import javax.annotation.Nonnull;
import java.util.UUID;

public class MyEconomyPlugin extends AbstractEconomy {
    private static MyEconomyPlugin instance;

    public MyEconomyPlugin(@Nonnull JavaPluginInit init) {
        super(init);
        instance = this;
    }

    public static MyEconomyPlugin get() {
        return instance;
    }

    @Override
    protected void setup() {
        // Register this economy implementation with Cassaforte
        boolean registered = Cassaforte.registerEconomy(this);
        if (!registered) {
            getLogger().warning("Another economy is already registered!");
            return;
        }
        getLogger().info("Economy registered successfully!");
    }

    @Override
    protected void shutdown() {
        // Clean up resources if needed
    }

    // Implement required economy methods
    @Override
    public EconomyResponse depositPlayer(UUID playerId, double amount) {
        // Your implementation
        return new EconomyResponse(amount, newBalance, EconomyResponse.ResponseType.SUCCESS);
    }

    @Override
    public double getBalance(UUID playerId) {
        // Your implementation
        return 0.0;
    }

    // ... implement other required methods
}

For Service Consumers

If you're using an economy, permission, or chat service in your plugin, retrieve the registered implementation through Cassaforte:

import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import it.cassaforte.api.Cassaforte;
import it.cassaforte.api.economy.Economy;
import it.cassaforte.api.permission.Permission;
import it.cassaforte.api.chat.Chat;
import javax.annotation.Nonnull;

public class MyPlugin extends JavaPlugin {
    private Economy economy;
    private Permission permission;
    private Chat chat;

    public MyPlugin(@Nonnull JavaPluginInit init) {
        super(init);
    }

    @Override
    protected void setup() {
        // Retrieve registered services
        economy = Cassaforte.getEconomy();
        permission = Cassaforte.getPermission();
        chat = Cassaforte.getChat();

        // Check if services are available
        if (economy != null && economy.isEnabled()) {
            getLogger().info("Economy service available: " + economy.getName());
        } else {
            getLogger().warning("No economy service available");
        }

        if (permission != null && permission.isEnabled()) {
            getLogger().info("Permission service available: " + permission.getName());
        } else {
            getLogger().warning("No permission service available");
        }

        if (chat != null) {
            getLogger().info("Chat service available");
        }
    }

    public void giveMoney(UUID playerId, double amount) {
        if (economy != null && economy.isEnabled()) {
            var response = economy.depositPlayer(playerId, amount);
            if (response.transactionSuccess()) {
                getLogger().info("Deposited " + amount + " to player");
            }
        } else {
            getLogger().warning("Cannot deposit: Economy not available");
        }
    }

    public boolean checkPermission(UUID playerId, String node) {
        if (permission != null && permission.isEnabled()) {
            return permission.playerHas(playerId, node);
        }
        return false;
    }

    public String formatPlayerName(UUID playerId, String playerName) {
        if (chat != null) {
            String prefix = chat.getPlayerPrefix(playerId, "");
            String suffix = chat.getPlayerSuffix(playerId, "");
            return prefix + playerName + suffix;
        }
        return playerName;
    }
}

Using Hytale Plugin Manager

You can also retrieve Cassaforte directly through the Hytale Plugin Manager:

import com.hypixel.hytale.server.core.plugin.PluginManager;
import com.hypixel.hytale.common.plugin.PluginIdentifier;
import it.cassaforte.api.Cassaforte;

public class MyPlugin extends JavaPlugin {

    @Override
    protected void setup() {
        // Method 1: Direct static access (recommended)
        Economy economy = Cassaforte.getEconomy();
        if (economy != null) {
            getLogger().info("Economy: " + economy.getName());
        }

        // Method 2: Access via PluginManager to get the plugin instance
        PluginManager pm = PluginManager.get();
        PluginIdentifier cassaforteId = new PluginIdentifier("it.cassaforte", "Cassaforte");

        if (pm.hasPlugin(cassaforteId)) {
            getLogger().info("Cassaforte plugin is loaded");
            // Services are accessed through Cassaforte static methods
            Economy economy2 = Cassaforte.getEconomy();
            Permission permission = Cassaforte.getPermission();
            Chat chat = Cassaforte.getChat();
        }
    }
}

API Reference

Cassaforte Registry

The it.cassaforte.api.Cassaforte class provides static methods for service registration and retrieval.

Registration Methods

// Register an economy implementation (returns false if already registered)
boolean success = Cassaforte.registerEconomy(Economy economy);

// Register a permission implementation
boolean success = Cassaforte.registerPermission(Permission permission);

// Register a chat implementation
boolean success = Cassaforte.registerChat(Chat chat);

Retrieval Methods

// Get registered economy (null if none registered)
Economy economy = Cassaforte.getEconomy();

// Get registered permission (null if none registered)
Permission permission = Cassaforte.getPermission();

// Get registered chat (null if none registered)
Chat chat = Cassaforte.getChat();

Economy API

import it.cassaforte.api.economy.Economy;
import it.cassaforte.api.economy.EconomyResponse;
import java.util.UUID;

// Check if player has an account
boolean hasAccount = economy.hasAccount(playerId);

// Get player balance
double balance = economy.getBalance(playerId);

// Deposit money
EconomyResponse response = economy.depositPlayer(playerId, 100.0);
if (response.transactionSuccess()) {
    // Success - new balance: response.balance
}

// Withdraw money
EconomyResponse response = economy.withdrawPlayer(playerId, 50.0);

// Check if player can afford
boolean canAfford = economy.has(playerId, 75.0);

// Bank operations (if supported)
if (economy.hasBankSupport()) {
    economy.createBank("MyBank", playerId);
    double bankBalance = economy.bankBalance("MyBank");
}

// Format currency
String formatted = economy.format(1000.50); // "$1,000.50"

Permission API

import it.cassaforte.api.permission.Permission;
import java.util.UUID;

// Player permissions
boolean hasPerm = permission.playerHas(playerId, "essentials.tp");
permission.playerAdd(playerId, "essentials.tp");
permission.playerRemove(playerId, "essentials.tp");

// Group permissions
boolean groupHas = permission.groupHas("admin", "essentials.*");
permission.groupAdd("admin", "essentials.*");
permission.groupRemove("admin", "essentials.*");

// Group membership
boolean inGroup = permission.playerInGroup(playerId, "vip");
permission.playerAddGroup(playerId, "vip");
permission.playerRemoveGroup(playerId, "vip");

// Get player's groups
List<String> groups = permission.getPlayerGroups(playerId);
String primaryGroup = permission.getPrimaryGroup(playerId);

Chat API

import it.cassaforte.api.chat.Chat;
import java.util.UUID;

// Player prefix/suffix
String prefix = chat.getPlayerPrefix(playerId, "");
String suffix = chat.getPlayerSuffix(playerId, "");
chat.setPlayerPrefix(playerId, "[Admin] ");
chat.setPlayerSuffix(playerId, " *");

// Group prefix/suffix
String groupPrefix = chat.getGroupPrefix("vip", "");
chat.setGroupPrefix("vip", "[VIP] ");

// Info nodes (metadata storage)
chat.setPlayerInfoString(playerId, "nickname", "Player1");
String nickname = chat.getPlayerInfoString(playerId, "nickname", "");

chat.setPlayerInfoInteger(playerId, "kills", 100);
int kills = chat.getPlayerInfoInteger(playerId, "kills", 0);

Project Structure

it.cassaforte.api/
├── Cassaforte.java               # Central service registry
├── economy/
│   ├── Economy.java              # Main economy interface
│   ├── AbstractEconomy.java      # Base implementation with defaults
│   └── EconomyResponse.java      # Operation response wrapper
├── permission/
│   └── Permission.java           # Permission and group management
└── chat/
    └── Chat.java                 # Chat formatting and metadata

Thread Safety

All Cassaforte registry methods are thread-safe. Registration uses atomic compare-and-set operations to ensure that only the first registration succeeds. Retrieval operations are lock-free and safe to call from any thread.

Best Practices

  1. Register Early: Always register your service in setup() or start() to ensure it's available before other plugins need it
  2. Handle Null Returns: Always check for null when retrieving services - they may not be registered
  3. Check isEnabled(): Verify isEnabled() before using economy or permission services
  4. Use Static Accessors: Provide a static getInstance() method in your plugin for easy access by other plugins
  5. Graceful Degradation: Your plugin should work even when optional services are unavailable

Building

./gradlew build

The build produces:

  • jar - Standard JAR with dependencies
  • shadowJar - Fat JAR with shaded dependencies (Gson relocated)

Requirements

  • Java 21+
  • Gradle 9.0+
  • Hytale Server API (compile-only)

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Inspired By

This project is inspired by VaultAPI for Bukkit/Minecraft, adapted for the Hytale platform.

Последние версии

Cassaforte-0.1.3.jar

Early Access 18.01.2026 322.0 КБ
Скачать

Cassaforte-0.1.2.jar

Early Access 18.01.2026 321.1 КБ
Скачать

Cassaforte-0.1.0.jar

Early Access 16.01.2026 320.7 КБ
Скачать