Skip to content

Basic usage

Jan edited this page Apr 9, 2023 · 10 revisions

Creating a spectator inventory

SpectatorInventorys are at the heart at InvSee++. They provide access to the target player's items.

Spectating the player's main inventory

To obtain the spectator inventory for the target, regardless whether the player is online or not, you can use the following snippet:

package com.example;

import com.janboerman.invsee.spigot.api.InvseeAPI;
import com.janboerman.invsee.spigot.api.MainSpectatorInventory;
import com.janboerman.invsee.spigot.api.response.*;

public class MyClass {

    private final MyPlugin myPlugin;

    public MyClass(MyPlugin myPlugin) { this.myPlugin = myPlugin; }

    public void createSpectatorInventory(InvseeAPI api, String targetPlayerName) {
        CompletableFuture<SpectateResponse<MainSpectatorInventory>> future = api.mainSpectatorInventory(targetPlayerName);
        future.whenComplete((response, error) -> {
            // The future completes in the server's primary thread, so it's safe to use Bukkit API methods.
    
            // Check whether an inventory could be created successfully:
            if (error == null) {
                if (response.isSuccess()) {
                    // Success!
                    MainSpectatorInventory spectatorInventory = response.getInventory();

                    // Now do whatever you want! :)

                    // For example, have an admin open it:
                    // org.bukkit.entity.Player adminPlayer = ...
                    // adminPlayer.openInventory(spectatorInventory);

                } else {
                    // The spectator inventory could not be created - this has a reason:
                    NotCreatedReason reason = response.getReason();
                    // Might want to use this reason in your diagnostics, e.g. customise a reply to a CommandSender, or log it.
                }
            } else {
                // A Java exception occurred when trying to create the inventory.
                // This should not occur, but if it does, here is an example of how to handle it:
                myPlugin.getLogger().log(Level.SEVERE, "Error when creating spectator inventory for: " + targetPlayerName, error);
            }
        }
    }

}

Let's go over some of the types now. The API method mainSpectatorInventory returns a CompletableFuture<SpectateResponse>. It returns a CompletableFuture because the API may need to perform some costly IO operations such as fetching the target's UUID from a Mojang's REST Api. This is too heavy to do on the server's primary thread.

SpectateResponse

Next up the SpectateResponse is a sum type of two cases:

  1. The inventory could be created successfully, in which case response.isSuccess() returns true and it is safe to call response.getInventory().
  2. The inventory could not be created, in which case response.isSuccess() return false and it is safe to call response.getReason().

You can think of SpectateResponse as similar to java.util.Optional, but with some additional information, should the optional be empty (the NotCreatedReason). Functional programmers may also recognise SpectateResponse<MainSpectatorInventory> as Either<NotCreatedReason, MainSpectatorInventory>.

We can also see that SpectateResponse is parameterised over the SpectatorInventory type: SpectateResponse<SI extends SpectatorInventory<?>>. In this example we used a MainSpectatorInventory, but there is another subtype of SpectatorInventory, which is EnderSpectatorInventory. As the name suggests, this inventory is used to spectate the items that are in the target player's EnderChest.

In summary, we have the following type hierarchies:

SpectateResponse
+--Succeed (with SpectatorInventory)
+--Fail (with NotCreatedReason)

org.bukkit.inventory.Inventory
+--SpectatorInventory
   +--MainSpectatorInventory
   +--EnderSpectatorInventory

NotCreatedReason
+--TargetDoesNotExist
+--TargetHasExemptPermission
+--OfflineSupportDisabled
+--ImplementationFault
+--UnknownReason

A note on NotCreatedReason

This list of reasons can not be relied upon as being exhaustive. In the future more NotCreatedReasons could be added. InvSee++ responds with these reasons in the following cases:

  • TargetDoesNotExist: If no player with the provided username or uuid exists. This reason hardly ever occurs since this only happens when the provided username is not registered to a Minecraft account at Mojang, or the plugin cannot connect to Mojang's REST Api.
  • TargetHasExemptPermission: If the target player is exempted from being spectated, i.e. they own the permission node invseeplusplus.exempt.invsee or invseeplusplus.exempt.endersee.
  • OfflineSupportDisabled: If offline-support is disabled in InvSee++'s config.yml (actually, it's more sophisticated than this, but we'll get to that later)
  • ImplementationFault: If for whatever reason the inventory provider failed (currently only happens for offline inventories from PerWorldInventory).
  • UnknownReason: This reason is currently used by the SpectateResponse#fromOptional(java.util.Optional) factory method, but this is not currently used inside InvSee++ itself.

Spectating the player's enderchest

Instead of calling api.mainSpectatorInventory(targetName) you simply call api.enderSpectatorInventory(targetName). Instead of a SpectateResponse<MainSpectatorInventory> you now get a SpectateResponse<EnderSpectatorInventory>. Easy as that! 😁