Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First attempt at allowing dynamic portals #118

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,23 @@ public static void randomSpawnPlayer(String server, UUID player) {
public static ConfigurationSection getConfigurationSection(JavaPlugin plugin, UUID uuid) {
return CustomWorldNBTStorage.getWorldNBTStorage().getConfigurationSection(uuid, plugin);
}

/**
* Registers a portal with BetterShards.
* This should be called for every custom portal you have.
* @param plugin_id The plugin specific id that your portal uses.
* @param plugin The plugin that is registering this portal.
* @param portal The portal Class.
* @param name The name of the portalType that will show up to players.
*/
public static <E extends Portal> void registerPortal(int plugin_id, String plugin, Class<E> portal, String name) {
BetterShardsPlugin.getDatabaseManager().addPortalType(plugin_id, plugin);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this is called from startup and repeatedly elsewhere, shouldn't you be checking if this type exists before adding it? Even though your uniqueness pkey guarantee will probably prevent a new row from being generated, relying on an error is risky... better to explicitly check imho.

// Now we get the generated id from bettershards.
int real_id = BetterShardsPlugin.getDatabaseManager().getPortalID(plugin, plugin_id);
BetterShardsPlugin.getPortalManager().getPortalFactory().registerPortal(real_id, portal, name);
}

public static int getPortalID(int pluginId, String plugin) {
return BetterShardsPlugin.getDatabaseManager().getPortalID(plugin, pluginId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ public void onEnable(){
db = new DatabaseManager();
pm = new PortalsManager();
pm.loadPortalsManager();
Bukkit.getScheduler().runTask(this, new Runnable() {

@Override
public void run() {
// Delay load so that other plugins can register with this plugin.
pm.loadPortalsFromServer();
}

});
CustomWorldNBTStorage.setWorldNBTStorage();
combatManager = new CombatTagManager(getServer());
randomSpawn = new RandomSpawnManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import vg.civcraft.mc.bettershards.portal.portals.CuboidPortal;
import vg.civcraft.mc.bettershards.portal.portals.WorldBorderPortal;
import vg.civcraft.mc.civmodcore.command.PlayerCommand;
import vg.civcraft.mc.mercury.MercuryAPI;

public class CreatePortal extends PlayerCommand {

Expand Down Expand Up @@ -45,19 +46,29 @@ else if (g.getMissingSelection() == GridLocation.RIGHTSELECTION)
else if (g.getMissingSelection() == GridLocation.LEFTSELECTION)
return sendPlayerMessage(p, ChatColor.RED + "Your primary selection has not been chosen.", true);
Portal portal = null;
if (args.length == 1 || args[1].equalsIgnoreCase("cuboid")) {
portal = new CuboidPortal(args[0], g.getLeftClickLocation(), g.getRightClickLocation(), null, true);
}
else if (args [1].equalsIgnoreCase("worldborder") || args [1].equalsIgnoreCase("wb")) {
LocationWrapper firstLoc = new LocationWrapper(g.getLeftClickLocation());
LocationWrapper secondLoc = new LocationWrapper(g.getRightClickLocation());
portal = new WorldBorderPortal(args[0],null, true, firstLoc, secondLoc);
}
else if (args [1].equalsIgnoreCase("circle") || args[1].equalsIgnoreCase("circular")) {
portal = new CircularPortal(args [0], null, true, g.getLeftClickLocation(), g.getRightClickLocation());
if (args.length < 1) {
String[] names = BetterShardsPlugin.getPortalManager().getPortalFactory().getAllPortalNames();
StringBuilder portals = new StringBuilder();
for (String x : names) {
portals.append(x);
portals.append(" ");
}
return sendPlayerMessage(p, ChatColor.RED + "You must specify a portaltype, portal types are: " + portals.toString(), true);
}

if (pm.getPortal(args[0]) != null)
return sendPlayerMessage(p, ChatColor.RED + "That portal name already exists.", true);


Class<? extends Portal> clazz = BetterShardsPlugin.getPortalManager().getPortalFactory().getPortal(args[1]);
portal = BetterShardsPlugin.getPortalManager().getPortalFactory().buildPortal(clazz);
portal.setName(args[0]);
portal.setIsOnCurrentServer(true);
portal.setServerName(MercuryAPI.serverName());
portal.setFirstLocation(new LocationWrapper(g.getLeftClickLocation()));
portal.setSecondLocation(new LocationWrapper(g.getRightClickLocation()));
portal.valuesPopulated();

pm.createPortal(portal);
String m = ChatColor.GREEN + "You have successfully created the portal " + args[0] + ".\n"
+ "To add a connection use the command /bsj <main portal> <connection>";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public boolean execute(CommandSender sender, String[] args) {
if (!one.getClass().equals(two.getClass())) {
return sendPlayerMessage(p, ChatColor.RED + "You can not join portals of a different type", true);
}
one.setPartnerPortal(two);
one.setPartnerPortal(two.getName());
String m = "%s has been set as Portal %s partner.";
sender.sendMessage(ChatColor.GREEN + String.format(m, two.getName(), one.getName()));
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import vg.civcraft.mc.bettershards.misc.LocationWrapper;
import vg.civcraft.mc.bettershards.misc.TeleportInfo;
import vg.civcraft.mc.bettershards.portal.Portal;
import vg.civcraft.mc.bettershards.portal.PortalFactory;
import vg.civcraft.mc.bettershards.portal.portals.CircularPortal;
import vg.civcraft.mc.bettershards.portal.portals.CuboidPortal;
import vg.civcraft.mc.bettershards.portal.portals.WorldBorderPortal;
Expand Down Expand Up @@ -101,6 +102,11 @@ public class DatabaseManager {
private static final String getAllBedLocation = "select * from player_beds;";
private static final String removeBedLocation = "delete from player_beds where uuid = ?;";

private static final String addPortalType = "insert ignore into portalVersion ("
+ "plugin_name, portal_plugin_id) values (?, ?);";
private static final String getPortalType = "select portal_id from portalVersion "
+ "where plugin_name = ? and portal_plugin_id = ?;";

private String cleanupLocks;

private BukkitTask lockCleanup;
Expand Down Expand Up @@ -221,6 +227,11 @@ public Boolean call() {
+ "inv_id INT NOT NULL,"
+ "last_upd TIMESTAMP NOT NULL DEFAULT NOW(),"
+ "PRIMARY KEY (uuid, inv_id));");
this.db.registerMigration(3, false, "CREATE TABLE IF NOT EXISTS portalVersion("
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is redundant with the portal_type column in the table "createPortalDataTable". It also doesnt convert over any preexisting portals

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't. portal_type doesn't offer any unique way for other plugins to know what ids are taken by who.
In portals manager it registers the portals there.

+ "portal_id INT NOT NULL AUTO_INCREMENT,"
+ "plugin_name VARCHAR(36) NOT NULL,"
+ "portal_plugin_id INT NOT NULL,"
+ "PRIMARY KEY (plugin_name, portal_plugin_id));");
}

@CivConfigs({
Expand Down Expand Up @@ -419,7 +430,7 @@ public void addPortalData(Portal portal, Portal connection){
PreparedStatement addPortalData = connect.prepareStatement(DatabaseManager.addPortalData);) {
addPortalData.setString(1, portal.getName());
addPortalData.setString(2, serverName);
addPortalData.setInt(3, portal.specialId);
addPortalData.setInt(3, portal.getPortalID());
String name = null;
if (connection != null)
name = connection.getName();
Expand Down Expand Up @@ -785,22 +796,20 @@ private Portal getPortalData(String name, LocationWrapper first, LocationWrapper
String serverName = set.getString("server_name");
String partner = set.getString("partner_id");
boolean currentServer = serverName.equals(MercuryAPI.serverName());
switch (specialId) {
case 0:
CuboidPortal p = new CuboidPortal(name, first.getFakeLocation(), second.getFakeLocation(), partner, currentServer);
p.setServerName(serverName);
return p;
case 1:
WorldBorderPortal wb = new WorldBorderPortal(name, partner, currentServer, first, second);
wb.setServerName(serverName);
return wb;
case 2:
CircularPortal cp = new CircularPortal(name, partner, currentServer, first.getFakeLocation(), second.getFakeLocation());
cp.setServerName(serverName);
return cp;
default:
return null;
}

PortalFactory factory = BetterShardsPlugin.getPortalManager().getPortalFactory();
Class<? extends Portal> clazz = factory.getPortal(specialId);

Portal p = factory.buildPortal(clazz);
p.setName(name);
p.setIsOnCurrentServer(true);
p.setServerName(MercuryAPI.serverName());
p.setFirstLocation(first);
p.setSecondLocation(second);
p.setPartnerPortal(partner);
p.valuesPopulated();

return p;
} catch (SQLException e) {
logger.log(Level.SEVERE, "Failed to getPortalData for {0}", name);
logger.log(Level.SEVERE, "Failed to getPortalData, exception:", e);
Expand Down Expand Up @@ -1019,6 +1028,64 @@ public void removeBed(UUID uuid) {
logger.log(Level.SEVERE, "Failed to removeBed, exception:", e);
}
}

/**
* This method is used to register with BetterShards what portal_id should
* be associated with each portal so when saving occurs each portal specific id
* is respected.
* This method should be called for each portal type that exists.
* So what should happen is in each plugin you give your custom portal it's own
* specific id that is internally recognized and tracked. From there you will pass
* that id and your plugin name to this method and it will create an id that can be
* used to pass in your constructor for your portal objects.
* After calling this method refer to the
* {@link #getPortalID(String, int)} method for getting what id to pass to
* your custom portal constructors.
* This method can be called at every start and if an id is already present it will
* fail silently.
* @param id The id that will be used internally in your plugin to reference each
* portal type.
* @param plugin Your plugin's name.
*/
public void addPortalType(int id, String plugin) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those ids should be managed in BetterShards, what you are doing here will require all possible users of this API to coordinate with each other to avoid overlap, which is exactly the opposite of what you want in an API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No they won't because when they get the portal id it will be specific in that specific server set up.
So better shards can create 3 portals and they will be given id 0,1,2.
Then some other plugin can create a portal and it will be given the id of 3.

try (Connection connection = db.getConnection();
PreparedStatement addPortalLoc = connection.prepareStatement(DatabaseManager.addPortalType)){
addPortalLoc.setString(1, plugin);
addPortalLoc.setInt(2, id);
addPortalLoc.execute();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth noting that inserts / updates / deletes can silently fail. Always use executeUpdate() and check the value returned. If != to the number of rows you thought you'd be modifying, you've got a failure-state.

}
catch (SQLException e) {
logger.log(Level.SEVERE, "Add PortalType DB failure: ", e);
}
}

/**
* This method is used to get an id that will be used in each portal's constructor.
* This id is important because it is used to map what kind of portal is being saved
* in the database.
* To use this method you must first call {@link #addPortalType(int, String)} in
* order to generate an id that can be used.
* @param plugin The plugin that is generating an id.
* @param id The plugin specific id that is used to reference what id
* should be returned based on what kind of portal it is.
* @return Should return an id that should be passed in your portal subclass constructor.
*/
public int getPortalID(String plugin, int id) {
int type_id = -1;
try (Connection connection = db.getConnection();
PreparedStatement addPortalLoc = connection.prepareStatement(DatabaseManager.getPortalType)){
addPortalLoc.setString(1, plugin);
addPortalLoc.setInt(2, id);
ResultSet set = addPortalLoc.executeQuery();
if (!set.next())
return -1;
type_id = set.getInt(1);
}
catch (SQLException e) {
logger.log(Level.SEVERE, "Get PortalType DB failure: ", e);
}
return type_id;
}

private void shortTrace() {
StackTraceElement[] ste = Thread.currentThread().getStackTrace();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@
import vg.civcraft.mc.bettershards.misc.PlayerStillDeadException;
import vg.civcraft.mc.bettershards.misc.TeleportInfo;
import vg.civcraft.mc.bettershards.portal.Portal;
import vg.civcraft.mc.bettershards.portal.portals.CircularPortal;
import vg.civcraft.mc.bettershards.portal.portals.CuboidPortal;
import vg.civcraft.mc.bettershards.portal.portals.WorldBorderPortal;
import vg.civcraft.mc.civmodcore.Config;
import vg.civcraft.mc.civmodcore.annotations.CivConfig;
import vg.civcraft.mc.civmodcore.annotations.CivConfigType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ else if (content[0].equals("portal")) {
Portal p2 = portalManager.getPortal(content[3]);
if (p1 == null || p2 == null)
return;
p1.setPartnerPortal(p2);
p1.setPartnerPortal(p2.getName());
}
else if (content[1].equals("remove")) {
Portal p1 = portalManager.getPortal(content[2]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,33 @@
import org.bukkit.World;
import org.bukkit.entity.Player;

import vg.civcraft.mc.bettershards.BetterShardsAPI;
import vg.civcraft.mc.bettershards.BetterShardsPlugin;
import vg.civcraft.mc.bettershards.database.DatabaseManager;
import vg.civcraft.mc.bettershards.external.MercuryManager;
import vg.civcraft.mc.bettershards.portal.Portal;
import vg.civcraft.mc.bettershards.portal.PortalFactory;
import vg.civcraft.mc.bettershards.portal.portals.CircularPortal;
import vg.civcraft.mc.bettershards.portal.portals.CuboidPortal;
import vg.civcraft.mc.bettershards.portal.portals.WorldBorderPortal;

public class PortalsManager {

private DatabaseManager db = BetterShardsPlugin.getDatabaseManager();
private Map<String, Portal> portals;
private Map<UUID, Long> arrivedPlayers = new ConcurrentHashMap<UUID,Long>();
private final long portalCoolDown = 10000L; //10 seconds
private PortalFactory factory;

public PortalsManager() {
super();
portals = new HashMap<String, Portal>();
factory = new PortalFactory();
registerParticleRunnable();
}

public void loadPortalsManager() {
loadPortalsFromServer();
generatePortalIds();
removeTeleportedPlayers();
autoSaveTimer();
}
Expand Down Expand Up @@ -175,4 +182,17 @@ public void run() {
}
}, 4L, 4L);
}

/**
* This method is used in order to generate ids for our portals.
*/
private void generatePortalIds() {
BetterShardsAPI.registerPortal(0, BetterShardsPlugin.getInstance().getName(), CuboidPortal.class, "Cuboid");
BetterShardsAPI.registerPortal(1, BetterShardsPlugin.getInstance().getName(), WorldBorderPortal.class, "WorldBorder");
BetterShardsAPI.registerPortal(2, BetterShardsPlugin.getInstance().getName(), CircularPortal.class, "Circle");
}

public PortalFactory getPortalFactory() {
return factory;
}
}
Loading