Arbeite an Gameplay-Systemen auf einem etablierten Minecraft-Netzwerk.
Als Developer bewerbenRegistrierte Spieler
Serverbetrieb
Custom Items & Systeme
Tausende Spielstunden
Ein Auktionssystem, das jede Woche drei alte Event-Items freigibt. Gebote funktionieren serverübergreifend in Echtzeit. Realisiert mit Redis Pub/Sub und Redis-Locks.
Serverübergreifendes Mailsystem für Spieler. Nachrichten können auch an offline Spieler gesendet werden und kosten Ingame-Währung.
Ein Spezialitem für Builder, das viele Bauaktionen vereinfacht und beschleunigt. Technisch umgesetzt mit der FastAsyncWorldEdit API.
Neue Spezialitems, Tools und Spielmechaniken für CityBuild.
Weiterentwicklung bestehender Systeme wie Auktionshaus, Jobs oder Events.
Umsetzung von Vorschlägen aus der Community oder dem Team - von kleinen QoL-Features bis zu neuen Spielmechaniken.
@CommandDefinition(
label = "info",
usage = "(Player)",
rank = PixelRank.SUPPORTER
)
public class InfoCommand extends PixelCommand<ExamplePlugin> {
public InfoCommand(ExamplePlugin plugin) {
super(plugin);
}
@Override
public void onPlayerCommand(BukkitPixelPlayer player, String label, String[] args) {
if (args.length != 1) {
player.sendMessage(getPlayerUsage());
return;
}
String targetName = args[0];
PlayerHandler playerHandler = BukkitCoreLibrary.getPlayerHandler();
BukkitPixelPlayer target = playerHandler.getPlayer(targetName);
// online player → direct
if (target != null) {
sendInformation(player, target);
return;
}
// offline player → async load
DataLoader loader = DataLoaderBuilder.create(DataLoaderBuilderType.SINGLE_PLAYER)
.name("InfoCommand")
.addRequireds(BasePlayerData.class, RankPlayerData.class)
.build();
BukkitExecutor.runAsync(getPlugin(), () -> {
try {
BukkitPixelPlayer loaded = playerHandler.getPlayer(targetName, loader);
BukkitExecutor.runSync(getPlugin(), () -> {
if (!player.isOnline()) return;
if (loaded == null) {
player.sendMessage(PLAYER_IS_NOT_REGISTERED);
return;
}
sendInformation(player, loaded);
});
} catch (Exception e) {
player.sendMessage(DATA_LOAD_FAILED);
}
});
}
private void sendInformation(BukkitPixelPlayer player, BukkitPixelPlayer target) {
BasePlayerData data = target.getData(BasePlayerData.class);
player.sendMessage(target.getColoredName() + " first join: " + data.getFirstJoin());
}
}
// basic custom item with persistent data
NamespacedKey usesKey = new NamespacedKey(plugin, "egg-uses");
ItemStack easterEgg = ItemBuilder.material(Material.EGG)
.name("<rainbow>Easteregg</rainbow>")
.lore(
"<gray>A rare drop of the easter event</gray>",
"<dark_gray>Limited uses</dark_gray>"
)
.glowing()
.unbreakable(true)
.persistentData(usesKey, PersistentDataType.INTEGER, 5)
.maximumAmount(1)
.build();
// enchantments (books vs normal items)
ItemStack protectionBook = ItemBuilder.enchantedBook(
Enchantment.PROTECTION
).build();
ItemStack customEnchantItem = ItemBuilder.material(Material.DIAMOND)
.enchantment(
Map.of(
CoreEnchantment.SOUL_BOUND, 1,
Enchantment.FORTUNE, 10
)
)
.build();
// player head (online player)
ItemStack playerHead = ItemBuilder.skull(player)
.name(player.getName() + "'s head")
.build();
// async skull loading (by any player name)
String playerName = "_Monty_";
BukkitExecutor.runAsync(plugin, () -> {
try {
SkullUtil.SkullData skullData = SkullUtil.getSkullData(playerName);
BukkitExecutor.runSync(plugin, () -> {
ItemStack customHead = ItemBuilder.material(Material.PLAYER_HEAD)
.anvilName(playerName + "'s head")
.skullTexture(skullData)
.build();
player.sendMessage("Loaded custom head for " + playerName);
});
} catch (Exception ignored) {}
});
public class ExampleInventory extends PixelPrivateInventory<ExamplePlugin> {
public ExampleInventory(ExamplePlugin plugin, Component title, InventoryType type, int size) {
super(plugin, title, type, size);
}
@Override
public void setItems() {
setItem(0, createItem(Material.DIAMOND, "<aqua>Reward</aqua>"));
setItem(1, createItem(Material.BARRIER, "<red>Close</red>"));
}
@Override
public void onClickInventory(InventoryClickEvent event) {
event.setCancelled(true);
ItemStack item = event.getCurrentItem();
if (item == null || item.getType() == Material.AIR) return;
BukkitPixelPlayer player = getPlayer();
if (event.getSlot() == 0) {
player.addOrDropItem(
ItemBuilder.material(Material.DIAMOND)
.name("<green>Reward claimed</green>")
.build()
);
player.playGenericClickSound();
}
if (event.getSlot() == 1) {
player.closeInventory();
}
}
private ItemStack createItem(Material material, String name) {
return ItemBuilder.material(material)
.name(name)
.glowing()
.build();
}
}
// opening
inventoryHandler.openInventory(
plugin,
player,
ExampleInventory.class,
"Example GUI",
9
);
public class SendBroadcastPacket extends PixelJedisPacket {
@Serial
private static final long serialVersionUID = 0L;
private final String gson;
public SendBroadcastPacket(Component component) {
this.gson = ComponentUtil.toGson(component);
}
public String getGson() {
return gson;
}
}
public class SendBroadcastListener extends PixelJedisPacketListener {
@JedisPacketListenerDefinition
public void onBroadcast(SendBroadcastPacket packet) {
Component component = ComponentUtil.fromGson(packet.getGson());
PlayerHandler playerHandler = BukkitCoreLibrary.getPlayerHandler();
for (BukkitPixelPlayer player : playerHandler.getOnlinePlayers()) {
player.sendMessage(component);
}
}
}
// packet sending
redisHandler.sendServerPacket(
PixelServerCategory.CITY_BUILD,
new SendBroadcastPacket(
ComponentBuilder.create("Server-wide message")
.hover("Just a test message!")
.build()
)
);
Du arbeitest weitgehend selbstständig an Features und bringst eigene Lösungen ein.
Entwicklung über Feature Branches und Merge Requests mit Code Reviews.
Die Core API ist in
unserer GitLab Wiki dokumentiert. Weitere Systeme werden schrittweise ergänzt.
Für Entwickler existiert ein separates Servernetzwerk zum Testen von Features. Änderungen können dort direkt ausprobiert werden, ohne den Livebetrieb zu beeinflussen.
Absprachen erfolgen unkompliziert über Discord oder Teamspeak.
Schicke uns eine Mail mit Infos zu dir und deinem Portfolio.
Wir sprechen kurz auf Discord über deine Erfahrung und offene Fragen.
Wenn alles passt, klären wir die Details und planen deinen Einstieg.
Schicke deine Bewerbung per Mail an:
[email protected]Wir melden uns innerhalb weniger Tage bei dir.