// via a player
Player player = ...;
Arena arena = GameAPI.get().getArenaByPlayer(player); // is null when player is a spectator and not inside the match
Arena arena = BedwarsAPI.getGameAPI().getArenaByPlayer(player); // exact same thing as line above
if(arena != null){
player.sendMessage("Hi, you're currently playing inside " + arena.getDisplayName());
}else{
// not inside an arena :/
}
// via a spectator
Player spectator = ...;
Arena arena = GameAPI.get().getArenaBySpectator(spectator);
// via a location
Location location = ...;
Arena arena = GameAPI.get().getArenaByLocation(location);
// by its name
String name1 = "Sandstorm";
String name2 = "sandstorm";
String arenaPicker = "%best%";
Arena arena = GameAPI.get().getArenaByName(name1);
Arena arena = GameAPI.get().getArenaByName(name2); // getByName is not as aggressive as getByExactName
Arena arena = GameAPI.get().getArenaByName(arenaPicker); // even allows the usage of arena pickers
Arena arena = GameAPI.get().getArenaByExactName(name1); // this one doesn't tho...
// get all arenas
for(Arena arena:GameAPI.get().getArenas()){
}
It works exactly the same as if you'd want to listen for Bukkit's events.
public class MyClass extends JavaPlugin implements Listener {
@Override
public void onEnable(){
Bukkit.getPluginManager().registerEvents(this, this);
}
@EventHandler
public void onPlayerJoinArena(PlayerJoinArenaEvent event){
event.getPlayer().sendMessage("Hey, how are you doing?");
}
}
Player player = ...;
PlayerDataAPI.get().getStats(player, stats -> {
// The PlayerStats object is basically a map with a String as its key and a Number as its value
// It contains all the info that shall be stored permanently, such as the amount of wons, loses, kills, etc.
// The exact keys of these entries isn't directly accessible, as this is being handled by a PlayerStatSets.
// A PlayerStatSet is basically a registerable entry in /bw stats. It has a name and it will return a value when asked for it.
// There's a PlayerStatSet for each type, like the rank, wons, loses, w/l etc.
// You could create your own /bw stats the following way
for(PlayerStatSet set:PlayerDataAPI.get().getRegisteredStatSets()){
player.sendMessage(set.getName() + ": " + set.getDisplayValue(stats));
}
// Would you like to change the value of a PlayerStatSet?
DefaultPlayerStatSet.KILLS.addValue(stats, 1); // increments the amount of kills by 1
});
Arena arena = ...;
if (arena.getGameWorld() == null) {
// game world hasn't been added yet or world is not loaded
return;
}
WorldStorage storage = BedwarsAPI.getWorldStorage(arena.getGameWorld());
// MBedwars uses a custom built entity system that does not actually spawn them within the world. These kind of entities are called "HologramEntity". Not to be confused by holographs.
for (HologramEntity he : storage.getHolograms()) {
if (he.getControllerType() != HologramControllerType.DEALER) // we only want dealers
continue;
if (!arena.isInside(he.getLocation())) // must be inside the arena corners
continue;
// Do whatever you want with him now. In our example we just change his name
he.setDisplayName("Penis");
}
Internally, all all informations of stat simply get stored in a Map, with the key being a String and the value a number. You may directly access them all using the PlayerStats object. However, this Map is only used for storing them. For displaying them in e.g. /bw stats, we've added something called stat sets. For each type (such as kills, wins, k/d, play time, rank, etc.) there's one instance that handles the way of how it shall be displayed and how it shall be displayed, as you for instance don't need to store a k/d. We're creating our own stat set, that gets displayed in /bw stats, with the following example:
ExampleStatSet class:
public class ExampleStatSet implements PlayerStatSet {
private static final String STORAGE_KEY = "example_bed_breaks_count";
private final Plugin plugin;
public ExampleStatSet(Plugin plugin) {
this.plugin = plugin;
}
@Override
public String getId() {
return "example_bed_breaks";
}
@Override
public Plugin getPlugin() {
return this.plugin;
}
@Override
public String getName(@Nullable CommandSender sender) {
return "Bed Breaks";
}
@Override
public String getDisplayedValue(PlayerStats stats) {
return formatInt/* Helper included in PlayerStatSet */(getValue(stats));
}
@Override
public Number getValue(PlayerStats stats) {
return stats.get(STORAGE_KEY);
}
////////////
// All the following methods are OPTIONAL. It's not a must to implement them, as it's sometimes not possible, such as for ranks.
@Override
public void setValue(PlayerStats stats, Number value) {
stats.set(STORAGE_KEY, value);
}
@Override
public void addValue(PlayerStats stats, Number amount){
stats.add(STORAGE_KEY, amount);
}
}
Within your main class:
@Override
public void onEnable() {
PlayerDataAPI.get().registerStatSet(new ExampleStatSet(this));
}
ExampleLobbyItemHandler class:
public class ExampleLobbyItemHandler extends LobbyItemHandler {
public ExampleLobbyItemHandler(Plugin plugin) {
super("example_handler" /* the id which will be specified in the configs */, plugin);
}
@Override
public void handleUse(Player player, Arena arena, LobbyItem item) {
// what shall happen when it's being used
player.sendMessage("Hello World");
}
@Override
public boolean isVisible(Player player, Arena arena, LobbyItem item) {
// in which cases it should be visible
return true;
// an other example:
// return player.hasPermission("xyz");
// player requires a specific permission to see the item
}
}
Within your main class:
@Override
public void onEnable() {
GameAPI.get().registerLobbyItemHandler(new ExampleLobbyItemHandler(this));
}
Within your lobby-hotbar.yml file:
cool-item:
name: "&eMy super cool item!"
slot: 3
handler: 'example_handler'
icon: 'dirt'
SpawnTemporaryZombieExample class:
public class SpawnTemporaryZombieExample implements SpecialItemUseHandler {
private final Plugin plugin;
public SpawnTemporaryZombieExample(Plugin plugin) {
this.plugin = plugin;
}
@Override
public Plugin getPlugin() {
return this.plugin;
}
@Override
public SpecialItemUseSession openSession(PlayerUseSpecialItemEvent event) {
// The purpose of this method is to construct a session that can be stopped at any time
// and to perform the action that shall happen when the Special Item is being used.
//
// Being able to stop a session at any time is very important to prevent them
// from functioning even after an arena has stopped.
final Session session = new Session(event);
session.run();
return session;
}
private static class Session extends SpecialItemUseSession {
private Entity entity;
private BukkitTask scheduler;
public Session(PlayerUseSpecialItemEvent event) {
super(event);
}
@Override
protected void handleStop() {
if (this.entity == null) // As we call stop() if the player is not looking at a block.
return;
// Lets clean up.
this.entity.remove();
this.scheduler.cancel();
}
protected void run() {
final Block clickedBlock = getEvent().getClickedBlock();
final BlockFace clickedBlockFace = getEvent().getClickedBlockFace();
// The player did not look at a block while he used the special item.
// Thus we don't know where to spawn the Zombie.
// But this is not a problem. We simply don't remove the item from his inventory in this case (by not calling takeItem()).
if (clickedBlock == null) {
// Very important! Otherwise there is a memory leak as the Session is being kept in a List until the end of the arena.
stop();
return;
}
// Lets spawn the Zombie.
final Location loc = clickedBlock.getRelative(clickedBlockFace).getLocation();
this.entity = loc.getWorld().spawnEntity(loc, EntityType.ZOMBIE);
// Have a scheduler that kills the Zombie after some time and calls stop() in case he died before that.
final AtomicInteger remainingSeconds = new AtomicInteger(10); // Zombie will live for 10 seconds.
this.scheduler = Bukkit.getScheduler().runTaskTimer(
getEvent().getSpecialItem().getPlugin(),
() -> {
if (!this.entity.isValid() /* he despawned or died */ || remainingSeconds.decrementAndGet() <= 0 /* 10 seconds passed */)
stop(); // this also calls this.handleStop()
},
20, 20
);
// And after everything went well we may take the item from his inventory.
takeItem();
}
}
}
Within your main class:
@Override
public void onEnable() {
final SpecialItem item = GameAPI.get().registerSpecialItem(
"temporary_zombie", /* id for the config file*/
this,
"Temporary Zombie", /* name */
Helper.get().parseItemStack("ZOMBIE_SPAWN_EGG") /* the item that will be given to the player */
);
if (item == null) // failed :( maybe there's already an item with the same id?
getLogger().warning("Failed to register special item temporary_zombie");
}
Within your shop.yml:
- name: 'Cool Zombie Item'
icon: 'ZOMBIE_SPAWN_EGG'
products:
- type: special-item
special-id: temporary_zombie
amount: 1
prices:
- type: spawner-item
spawner-id: iron
amount: 30
ExampleShopLayout class:
public class ExampleShopLayout implements ShopLayoutHandler {
private static final ItemStack GLASS_CENTER = Helper.get().parseItemStack("BLUE_STAINED_GLASS_PANE {DisplayName:\" \"}");
@Override
public GUI build(OpenEvent event) {
final Player player = event.getPlayer();
final ChestGUI gui = new ChestGUI(0, event.getDefaultGUITitle());
// pages
if (event.getOpenPage() != null) {
event.getOpenPage().setIcon(NMSHelper.get().setGlowEffect(event.getOpenPage().getIcon(), true));
gui.setTitle(event.getOpenPage().getDisplayName());
}
for (ShopPage page : event.getPages())
gui.addItem(event.build(page));
// items
if (event.getOpenPage() != null) {
// glass
gui.setHeight(gui.getHeight() + 1);
for (int x = 0; x < 9; x++)
gui.setItem(GLASS_CENTER, x, gui.getHeight() - 1);
// items
int height = gui.getHeight() - 2;
for (int iRow = 0; iRow < (event.getPageItems().size() + 8) / 9; iRow++) {
if (gui.getHeight() + 2 > 6) {
getLogger(event).warning("The shop layout does not support that amount of content!");
break;
}
final List<ShopItem> items = new ArrayList<>();
final int length = Math.min(event.getPageItems().size() - iRow * 9, 9);
for (int i = 0; i < length; i++)
items.add(event.getPageItems().get(i + iRow * 9));
gui.setHeight(gui.getHeight() + 2);
height += 2;
int x = 0;
for (ShopItem i : items) {
if (i.getPrices().size() == 1) {
final ShopPrice price = i.getPrices().get(0);
gui.setItem(
setDisplayName(
price.getDisplayItem(player),
ChatColor.WHITE + price.getDisplayName(player)),
x,
height);
} else if (i.getPrices().size() >= 2)
getLogger(event).warning("The shop layout only displays 1 price!");
gui.setItem(event.build(i), x, height + 1);
x++;
}
}
}
gui.formatAnyRow(CenterFormat.ALIGNED);
return gui;
}
private static Logger getLogger(OpenEvent event) {
return event.getLayout().getPlugin().getLogger();
}
private static ItemStack setDisplayName(ItemStack is, String name) {
final ItemMeta im = is.getItemMeta();
im.setDisplayName(name);
is.setItemMeta(im);
return is;
}
}
Within your main class:
@Override
public void onEnable() {
GameAPI.get().registerShopLayout(new ShopLayout() {
final ExampleShopLayout handler = new ExampleShopLayout();
@Override
public String getName() {
return "ExampleLayout"; // the name within your shop.yml
}
@Override
public boolean isBeta() {
return false;
}
@Override
public Plugin getPlugin() {
return Plugin.this;
}
@Override
public ShopLayoutHandler getHandler() {
return this.handler;
}
});
}
Within your shop.yml:
layout-name: 'ExampleLayout'
Register your custom Parties plugin so that MBedwars takes over the handling of i.a. automatically moving the party members as well to the arena, once a leader joins one.
Example with PartyAndFriends Spigot extended:
public class PartyAndFriendsHook implements PartiesHook {
private final Plugin managingPlugin, hookedPlugin;
public PartyAndFriendsParty(JavaPlugin plugin) {
this.managingPlugin = plugin;
this.hookedPlugin = Bukkit.getPluginManager().getPlugin("PartyAndFriends");
}
/**
Your plugin instance.
**/
public Plugin getManagingPlugin() {
return this.managingPlugin;
}
/**
Get the plugin instance that actually provides the Parties system.
May be null if it's e.g. from a BungeeCord plugin.
May also be the same as getManagingPlugin() if your plugin is actually a Parties plugin that added support for MBedwars.
**/
@Nullable
public Plugin getHookedPlugin() {
return this.hookedPlugin;
}
public void getMember(UUID playerUUID, Consumer<Optional<PartiesHook.Member>> callback) {
Validate.notNull(playerUUID, "playerUUID");
Validate.notNull(callback, "callback");
// try to obtain the party info from PartyAndFriends
PlayerParty party = PartyManager.getInstance().getParty(playerUUID);
// is the player not a part of a party? then we are done here. don't forget to tell that the callback!
if (party == null) {
callback.accept(Optional.empty()); // Optional.empty() means that there is no party info
return;
}
// player is a member of a party. copy all data over
WrappedParty wrappedParty = new WrappedParty();
List<Member> allMembers = new ArrayList<>();
for (OnlinePAFPlayer member : party.getPlayers() /* PaF doesn't include the leader */)
allMembers.add(new WrappedMember(member.getUniqueId(), member.getName(), false, wrappedParty));
if (party.getLeader() != null)
allMembers.add(new WrappedMember(party.getLeader().getUniqueId(), party.getLeader().getName(), true, wrappedParty));
wrappedParty.setMembers(allMembers); // don't forget
// now we just have to return the member instance from the party
Member member = wrappedParty.getMember(playerUUID);
if (member == null) // making sure you didn't do a mistake
throw new IllegalStateException("Player is not a part of the constructed Party instance");
callback.accept(Optional.of(member));
}
///// You may just copy & paste this part without modifying anything below here
private static class WrappedParty implements Party {
Collection<Member> allMembers, leaders, membersWithoutLeaders;
@Override
public Collection<Member> getLeaders() {
return this.leaders;
}
@Override
public Collection<Member> getMembers(boolean includeLeaders) {
if (includeLeaders)
return this.allMembers;
else
return this.membersWithoutLeaders;
}
void setMembers(Collection<Member> allMembers /* also includes leaders*/) {
this.allMembers = Collections.unmodifiableCollection(allMembers);
this.leaders = allMembers.stream()
.filter(Member::isLeader)
.collect(Collectors.toList());
this.membersWithoutLeaders = allMembers.stream()
.filter(m -> !m.isLeader())
.collect(Collectors.toList());
}
}
private static class WrappedMember implements Member {
final Party party;
final UUID uniqueId;
final String username;
final boolean leader;
public WrappedMember(UUID uuid, String username, boolean leader, Party party) {
this.uniqueId = uuid;
this.username = username;
this.leader = leader;
this.party = party;
}
@Override
public Party getParty() {
return this.party;
}
@Override
public UUID getUniqueId() {
return this.uniqueId;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isLeader() {
return this.leader;
}
}
}
Within your main class:
@Override
public void onEnable() {
HookAPI.get().registerPartiesHook(new PartyAndFriendsHook(this));
}