SimonSays - A Premium minigame!
SimonSays a minigame for Paper/Spigot servers.SimonSays brings the classic "Simon says" game into Minecraft as a competitive arena minigame. Players join an arena, receive fast instructions and must react before time runs out. Trap rounds make it harder: if Simon did not say it, obeying can eliminate you.
Requirements
- Spigot/Paper/Purpur/Fork server.
- Java 25 or newer.
- PacketEvents for advanced NPC support.
- DecentHolograms for leaderboard holograms.
- PlaceholderAPI for external placeholders.
- MySQL for multi-server or Velocity networks.
Features
- Unlimited arenas.
- Lobby, game spawn, spectator spawn, center, radius and color floor per arena.
- Normal rounds, trap rounds and combo rounds.
- Movement, interaction, chat, direction, block, item, NPC, animal and color tasks.
- Inventory, experience, food and game mode save/restore.
- Persistent statistics with SQLite or MySQL.
- Rewards for wins, participation and survived rounds.
- Cosmetics shop with coins, particles, glow and sounds.
- Scoreboard, bossbar, actionbar, titles, sounds and configurable effects.
- Join signs, NPCs and GUI arena selector.
- Leaderboard holograms with DecentHolograms.
- PlaceholderAPI support for stats, arenas and leaderboards.
- Velocity support for lobby routing and shared MySQL data.
- Public API for custom tasks from other plugins.
Installation
- Buy the plugin
- Download and place in to your /plugins folder
- Put dependencies described
- Start your server
- Configure config.yml, tasks.yml, rewards.yml, sounds.yml, scoreboard.yml and the files inside lang/.
- Use /ss reload to reload changes without restarting.
Create an Arena
/ss arena create arena1/ss arena setspawn arena1
/ss arena setlobby arena1
/ss arena setgame arena1
/ss arena setspectator arena1
/ss arena setcenter arena1 15
/ss arena setfloor arena1 pos1
/ss arena setfloor arena1 pos2
/ss arena preview arena1
/ss arena enable arena1
Player Commands
- /ss join <arena> - join an arena.
- /ss quickjoin - join the best available arena.
- /ss leave - leave the current game.
- /ss list - list available arenas.
- /ss menu - open the arena selector.
- /ss shop - open the shop.
- /ss stats [arena] - view statistics.
- /ss top <category> [arena] - view leaderboards.
Admin Commands
- /ss arena create <id> - create an arena.
- /ss arena delete <id> - delete an arena.
- /ss arena setspawn <id> - set the main spawn.
- /ss arena setlobby <id> - set the waiting lobby.
- /ss arena setgame <id> - set the game spawn.
- /ss arena setspectator <id> - set the spectator spawn.
- /ss arena setcenter <id> <radius> - set center and radius.
- /ss arena setfloor <id> <pos1|pos2> - set the color floor.
- /ss arena enable <id> - enable an arena.
- /ss arena disable <id> - disable an arena.
- /ss arena set <id> <key> <value> - override settings per arena.
- /ss arena unset <id> <key> - clear an arena override.
- /ss arena settasks <id> <ids|*> - limit arena tasks.
- /ss arena settings <id> - show arena settings.
- /ss arena preview <id> - show setup particles.
- /ss forcestart - start the current game.
- /ss npc create <arena> [name] - create a join NPC.
- /ss npc remove - remove the nearest NPC.
- /ss hologram create <category> [arena] - create a hologram.
- /ss hologram remove <id> - remove a hologram.
- /ss hologram remove arena <arena> - remove holograms for an arena.
- /ss hologram list - list holograms.
- /ss reload - reload configuration.
Permissions
- simonsays.play - allows players to join and play. Default: everyone.
- simonsays.admin - allows full plugin administration. Default: operators.
Main Files
- config.yml - game rules, difficulty, spectators, network, shop and placeholders.
- tasks.yml - enabled tasks and selection weights.
- rewards.yml - rewards and milestones.
- sounds.yml - minigame sounds.
- scoreboard.yml - lobby and game scoreboards.
- lang/en.yml and lang/es.yml - MiniMessage language files.
- arenas.yml - arenas saved automatically.
SimonSays API
Available Events
All events extend SimonEvent and provide access to the current Game.| Event | Description | Extra data |
|---|---|---|
| SimonGameStartEvent | Fired when a game starts | NA |
| SimonGameEndEvent | Fired when a game ends | NA |
| SimonRoundStartEvent | Fired at the start of each round | int getRound() |
| SimonRoundEndEvent | Fired after a round is validated | int getRound() |
| SimonPlayerEliminatedEvent | Fired when a player is eliminated | Player getPlayer(), int getRound() |
| SimonWinnerEvent | Fired when a game produces a winner | Player getWinner(), int getPoints() |
Base class SimonEvent
public abstract class SimonEvent extends Event {
public Game getGame(); // Game associated with this event
public HandlerList getHandlers();
public static HandlerList getHandlerList();
}SimonSaysAPI — Public facade
SimonSaysAPI api = SimonSaysAPI.get();// Arenas
Arena getArena(String id);
Collection<Arena> getArenas();// Players
Game getGame(Player player);
boolean isPlaying(Player player);
CompletableFuture<PlayerStats> getStats(UUID uuid, String name);// Tasks
TaskRegistry getTaskRegistry();
void registerTask(Task task);
void registerTaskProvider(Consumer<TaskRegistry> provider);// Rewards
void grantReward(Player player, String key);// Network (BungeeCord/Velocity)
void sendToServer(Player player, String server);
void quickJoin(Player player);TaskBuilder — Create custom tasks
TaskBuilder.create("my-task")
.instruction("<yellow>Do something!</yellow>") // MiniMessage
.weight(5) // Selection weight
.onStart((game, gp, ctx) -> { // When the round starts
ctx.put("target", randomLocation);
ctx.addCleanup(() -> removeProp()); // When the round ends
})
.check((game, gp, ctx) -> { // Evaluated every tick
Location target = ctx.get("target");
return gp.bukkit().getLocation().distance(target) < 3;
})
.register();Evaluation modes
| Method | Behavior |
|---|---|
| .check(...) (default) | Passes if the condition holds at ANY tick during the window |
| .evaluateAtEnd() | Only evaluated when the window closes (e.g. "don't move" tasks) |
| .passWhenMarked() | Passes when your listener calls game.currentContext().complete(uuid) |
Example — Custom event and external task
1. Define a custom event
package com.example.myaddon;
import dev.blancocl.simonSays.api.SimonEvent;
import dev.blancocl.simonSays.game.Game;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
public class PlayerJumpEvent extends SimonEvent {
private static final HandlerList HANDLERS = new HandlerList();
private final Player player;
public PlayerJumpEvent(Game game, Player player) {
super(game);
this.player = player;
}
public Player getPlayer() {
return player;
}
@Override
public HandlerList getHandlers() {
return HANDLERS;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
}2. Register a listener that fires the event
package com.example.myaddon;
import dev.blancocl.simonSays.api.SimonSaysAPI;
import dev.blancocl.simonSays.game.Game;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
public class JumpListener implements Listener {
@EventHandler
public void onMove(PlayerMoveEvent e) {
Player player = e.getPlayer();
if (!SimonSaysAPI.get().isPlaying(player)) return;
// Detect jump (upward Y change)
if (e.getFrom().getY() < e.getTo().getY()) {
Game game = SimonSaysAPI.get().getGame(player);
PlayerJumpEvent event = new PlayerJumpEvent(game, player);
player.getServer().getPluginManager().callEvent(event);
}
}
}3. Use the API from another plugin
package com.example.myaddon;
import dev.blancocl.simonSays.api.SimonSaysAPI;
import dev.blancocl.simonSays.api.TaskBuilder;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class MyAddon extends JavaPlugin implements Listener {
@Override
public void onEnable() {
// Register task: "jump 3 times"
TaskBuilder.create("triple-jump")
.instruction("<aqua>Jump <yellow>3</yellow> times!</aqua>")
.weight(6)
.onStart((game, gp, ctx) -> ctx.put("jumps", 0))
.check((game, gp, ctx) -> {
Integer jumps = ctx.get("jumps");
return jumps != null && jumps >= 3;
})
.register();
getServer().getPluginManager().registerEvents(this, this);
}
@EventHandler
public void onJump(PlayerJumpEvent e) {
if (!SimonSaysAPI.get().isPlaying(e.getPlayer())) return;
Game game = e.getGame();
game.currentContext().ifPresent(ctx -> {
Integer jumps = ctx.get("jumps");
ctx.put("jumps", jumps == null ? 1 : jumps + 1);
});
}
}4. Addon plugin.yml
name: MySimonSaysAddon
version: 1.0.0
main: com.example.myaddon.MyAddon
depend: [SimonSays]SimonSays Placeholders
Requires: PlaceholderAPIPrefix: %simonsays_...%
Player Stats
| Placeholder | Description | Aliases |
|---|---|---|
| %simonsays_wins% | Total wins | NA |
| %simonsays_losses% | Total losses | NA |
| %simonsays_games% | Total games played | NA |
| %simonsays_streak% | Best streak | best_streak |
| %simonsays_coins% | Coins | NA |
| %simonsays_xp% | XP | NA |
| %simonsays_tasks_completed% | Tasks completed | completed |
| %simonsays_tasks_failed% | Tasks failed | failed |
| %simonsays_playtime% | Playtime | Xh Xm or Xm |
Arena (Live State)
| Placeholder | Description |
|---|---|
| %simonsays_arena_<id>_state% | Arena state (WAITING, PLAYING, etc.) |
| %simonsays_arena_<id>_name% | Arena display name |
| %simonsays_arena_<id>_players% | Current player count |
| %simonsays_arena_<id>_max% | Max players |
| %simonsays_arena_<id>_min% | Min players |
| %simonsays_arena_<id>_round% | Current round number |
| %simonsays_arena_<id>_seconds% | Seconds left |
%simonsays_arena_lobby1_state% -> WAITING
%simonsays_arena_lobby1_players% -> 3
Global Leaderboard
| Placeholder | Description |
|---|---|
| %simonsays_top_<category>_<rank>_name% | Player name at rank |
| %simonsays_top_<category>_<rank>_value% | Value at rank |
Example:
%simonsays_top_wins_1_name% -> Notch
%simonsays_top_wins_1_value% -> 142
%simonsays_top_coins_3_name% -> Steve
%simonsays_top_coins_3_value% -> 5800
Per Arena Leaderboard
| Placeholder | Description |
|---|---|
| %simonsays_topa_<arena><category><rank>_name% | Player name at rank |
| %simonsays_topa_<arena><category><rank>_value% | Value at rank |
Example:
%simonsays_topa_lobby1_wins_1_name% -> Notch
%simonsays_topa_lobby1_wins_1_value% -> 10
%simonsays_topa_arena2_playtime_2_name% -> Herobrine
%simonsays_topa_arena2_playtime_2_value% -> 2h 15m
For arenas setup, and more info:
https://simonsays.blancocl.dev/en/
