Skip to content
Merged
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
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.proc>full</maven.compiler.proc>
<coreprotect.version>25.0.0</coreprotect.version>
<tlibs.version>2.0.0</tlibs.version>
<tlibs.version>2.4.1</tlibs.version>
</properties>

<dependencies>
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/net/tfminecraft/vehicleframework/VFLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ public static void log(String error) {
Bukkit.getLogger().warning("[VehicleFramework] Error! " + error);
}

public static void severe(String error) {
Bukkit.getLogger().severe("[VehicleFramework] " + error);
}

public static void info(String info) {
Bukkit.getLogger().info("[VehicleFramework] " + info);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package net.tfminecraft.vehicleframework;

import java.io.File;
import java.util.Locale;

import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;

import net.tfminecraft.coreprotect.CoreProtect;
import net.tfminecraft.coreprotect.CoreProtectAPI;
import net.tfminecraft.vehicleframework.api.OwnershipIdentity;
import net.tfminecraft.vehicleframework.cache.Cache;
import net.tfminecraft.vehicleframework.database.LogWriter;
import net.tfminecraft.vehicleframework.database.PersistenceLog;
Expand All @@ -24,6 +26,8 @@
import net.tfminecraft.vehicleframework.loaders.DeathTemplateLoader;
import net.tfminecraft.vehicleframework.managers.CommandManager;
import net.tfminecraft.vehicleframework.managers.VehicleManager;
import net.tfminecraft.vehicleframework.ownership.CharacterLookup;
import net.tfminecraft.vehicleframework.ownership.OwnershipMode;
import net.tfminecraft.vehicleframework.protocol.VehiclePacketListener;
import org.bstats.bukkit.Metrics;
import net.tfminecraft.vehicleframework.util.MythicMobsIntegration;
Expand Down Expand Up @@ -60,6 +64,7 @@ public class VehicleFramework extends JavaPlugin{
private static TrackRegistry trackRegistry;
private static TrackDisplayManager trackDisplayManager;
private static VehiclePacketListener packetListener;
private String ownershipValue = "";

@Override
public void onEnable() {
Expand All @@ -81,6 +86,7 @@ public void onEnable() {
}
createConfigs();
loadConfigs();
configureOwnership();
Cache.applyTrackDisplayStyle();
VFLogger.info("Starting systems...");
registerListeners();
Expand Down Expand Up @@ -117,6 +123,7 @@ public void onDisable() {
}
public void registerListeners() {
getServer().getPluginManager().registerEvents(vehicleManager, this);
getServer().getPluginManager().registerEvents(vehicleManager.getOwnershipManager(), this);
getServer().getPluginManager().registerEvents(vehicleManager.getSpawnManager(), this);
getServer().getPluginManager().registerEvents(vehicleManager.getRepairManager(), this);
getServer().getPluginManager().registerEvents(new TrackToolListener(), this);
Expand All @@ -131,6 +138,16 @@ public void registerListeners() {
packetListener = listener;
listener.register();
}
/** Reads the identity mode once; a reload does not change it. */
void configureOwnership() {
String value = configLoader.readOwnershipIdentity(new File(getDataFolder(), "config.yml"));
ownershipValue = normalised(value);
boolean characters = getServer().getPluginManager().getPlugin(OwnershipMode.CHARACTER_PLUGIN) != null;
if (OwnershipMode.configure(value, characters) == OwnershipIdentity.CHARACTERS) {
CharacterLookup.probe();
}
}

public void startManagers() {
vehicleManager.start();
}
Expand Down Expand Up @@ -294,13 +311,26 @@ public void reload() {
Cache.removeLights();
Cache.removeProjectiles();
loadConfigs();
warnIfOwnershipChanged();
vehicleManager.reload();
if (trackDisplayManager != null) {
trackDisplayManager.reloadSwitches();
}
PersistenceLog.append("RELOAD_END pendingSpawns cycle=" + PersistenceLog.spawnCycle());
}

private void warnIfOwnershipChanged() {
String value = normalised(configLoader.readOwnershipIdentity(new File(getDataFolder(), "config.yml")));
if (!value.equals(ownershipValue)) {
VFLogger.log(OwnershipMode.CONFIG_KEY + " is read at startup only; ownership stays "
+ OwnershipMode.identity().name().toLowerCase(Locale.ROOT) + " until a restart.");
}
}

private static String normalised(String value) {
return value == null ? "" : value.trim().toLowerCase(Locale.ROOT);
}

//Access we dont need static variables all over the place:
public static TrackRegistry getTrackRegistry() {
return trackRegistry;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package net.tfminecraft.vehicleframework.api;

import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
* The owner a system write sets, plus the seeded whitelist entries for {@link SystemReason#MIGRATE}.
*
* @param owner {@code none}, {@code char_none} or {@code char_<characterId>}, stored in canonical
* form
* @param whitelist the entries to store; only accepted with {@code MIGRATE}, {@code null} keeps the
* current entries
*/
public record OwnerChange(String owner, List<String> whitelist) {

public OwnerChange {
Objects.requireNonNull(owner, "owner");
String given = owner;
owner = VehicleOwners.canonical(given).orElseThrow(
() -> new IllegalArgumentException("Not a character-mode owner value: " + given));
whitelist = whitelist == null ? null : List.copyOf(whitelist);
}

/** Owned by the character. */
public static OwnerChange character(UUID characterId) {
return new OwnerChange(VehicleOwners.character(characterId), null);
}

/** A faction vehicle without a leader character; nobody matches it and nobody can claim it. */
public static OwnerChange noCharacter() {
return new OwnerChange(VehicleOwners.NO_CHARACTER, null);
}

/** Unowned and claimable. */
public static OwnerChange unowned() {
return new OwnerChange(VehicleOwners.NONE, null);
}

/** The migration seed: owner plus the decided whitelist entries. */
public static OwnerChange seeded(String owner, List<String> whitelist) {
return new OwnerChange(owner, Objects.requireNonNull(whitelist, "whitelist"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package net.tfminecraft.vehicleframework.api;

/**
* How vehicle owners are identified, read once at enable from the {@code ownership-identity}
* config key.
*/
public enum OwnershipIdentity {
/** Owners and whitelist entries are characters ({@code char_<characterId>}). */
CHARACTERS,
/** Owners and whitelist entries are account names ({@code player_<name>}); no RPCharacters. */
NAMES,
/**
* The key is missing while RPCharacters is installed, or holds an unknown value. Every
* ownership feature is refused until the key is set and the server restarts.
*/
DISABLED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package net.tfminecraft.vehicleframework.api;

import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

/**
* One vehicle's ownership as stored in SQLite and as held by its live object, kept apart so a
* verifier can compare them.
*
* @param vehicleId the vehicle
* @param typeId the vehicle type, from the live object when loaded
* @param name the vehicle name, from the live object when loaded
* @param storedOwnerColumn the SQLite {@code owner} column, or {@code null} without a stored row
* @param stored the stored payload's fields; empty without a row or when the payload
* cannot be read
* @param live the live object's fields; empty when the vehicle is not loaded
*/
public record OwnershipMeta(UUID vehicleId, String typeId, String name, String storedOwnerColumn,
Optional<OwnershipValues> stored, Optional<OwnershipValues> live) {

public OwnershipMeta {
Objects.requireNonNull(vehicleId, "vehicleId");
stored = stored == null ? Optional.empty() : stored;
live = live == null ? Optional.empty() : live;
}

/** Whether a non-tombstoned SQLite row exists. */
public boolean hasStoredRow() {
return storedOwnerColumn != null;
}

/** Whether the vehicle is loaded. */
public boolean isLive() {
return live.isPresent();
}

/** The live fields when loaded, otherwise the stored ones. */
public Optional<OwnershipValues> current() {
return live.isPresent() ? live : stored;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package net.tfminecraft.vehicleframework.api;

/** Why a vehicle's owner, whitelist or tickets changed. */
public enum OwnershipReason {
/** A player claimed an unowned vehicle, including a staff takeover of an unowned one. */
CLAIM,
/** The owner removed their ownership. */
CLEAR,
/** Staff took over a vehicle that already had an owner. */
TAKEOVER,
WHITELIST_ADD,
WHITELIST_REMOVE,
WHITELIST_TOGGLE,
/** Tickets toggled on the vehicle's ticket source (the locomotive of a coupled car). */
TICKETS_TOGGLE,
MIGRATE,
RESTAMP,
RELEASE,
HANDOVER,
CONSTRUCT,
TRANSFER;

/** Whether this reason comes from {@link VehicleFrameworkApi#setOwnership}. */
public boolean isSystem() {
return ordinal() >= MIGRATE.ordinal();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package net.tfminecraft.vehicleframework.api;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
* Immutable copy of a vehicle's ownership fields.
*
* @param owner the owner value, {@code none} when blank
* @param whitelist raw whitelist entries in stored order
* @param whitelisted whether the whitelist is enforced
* @param ticketsEnabled whether passenger seats need a ticket
* @param ticketId the current ticket ID, or {@code null}
*/
public record OwnershipValues(String owner, List<String> whitelist, boolean whitelisted, boolean ticketsEnabled,
String ticketId) {

public OwnershipValues {
owner = owner == null || owner.isBlank() ? VehicleOwners.NONE : owner;
List<String> entries = new ArrayList<>();
if (whitelist != null) {
whitelist.stream().filter(Objects::nonNull).forEach(entries::add);
}
whitelist = List.copyOf(entries);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package net.tfminecraft.vehicleframework.api;

import java.util.Objects;
import java.util.UUID;

/**
* A vehicle whose owner could not be migrated and keeps its legacy value.
*
* @param vehicleId the vehicle
* @param owner its current owner value
* @param reason a short staff-facing reason
*/
public record QuarantinedVehicle(UUID vehicleId, String owner, String reason) {

public QuarantinedVehicle {
Objects.requireNonNull(vehicleId, "vehicleId");
owner = owner == null ? "" : owner;
reason = reason == null ? "" : reason;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package net.tfminecraft.vehicleframework.api;

/**
* Reasons accepted by {@link VehicleFrameworkApi#setOwnership}. Each decides what happens to the
* whitelist entries and the whitelist flag; tickets are never changed.
*
* <table>
* <caption>Whitelist disposition</caption>
* <tr><th>Reason</th><th>Entries</th><th>Flag</th></tr>
* <tr><td>RESTAMP</td><td>kept</td><td>kept</td></tr>
* <tr><td>MIGRATE</td><td>as seeded ({@link OwnerChange#whitelist()}, kept when null)</td><td>kept</td></tr>
* <tr><td>CONSTRUCT</td><td>emptied</td><td>on if {@code allow-whitelist} and {@code whitelisted-by-default}</td></tr>
* <tr><td>RELEASE, HANDOVER</td><td>emptied</td><td>off</td></tr>
* <tr><td>TRANSFER</td><td>emptied</td><td>kept</td></tr>
* </table>
*/
public enum SystemReason {
MIGRATE(OwnershipReason.MIGRATE),
RESTAMP(OwnershipReason.RESTAMP),
RELEASE(OwnershipReason.RELEASE),
HANDOVER(OwnershipReason.HANDOVER),
CONSTRUCT(OwnershipReason.CONSTRUCT),
TRANSFER(OwnershipReason.TRANSFER);

private final OwnershipReason reason;

SystemReason(OwnershipReason reason) {
this.reason = reason;
}

/** The reason carried by {@link net.tfminecraft.vehicleframework.events.VehicleOwnershipChangedEvent}. */
public OwnershipReason reason() {
return reason;
}
}
Loading
Loading