Skip to content
Draft
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 @@ -139,13 +139,39 @@ public void removeSubLevel(final int x, final int z, final SubLevelRemovalReason

super.removeSubLevel(x, z, reason);

if (reason == SubLevelRemovalReason.REMOVED) {
if (reason.clearsOccupancy()) {
final ServerLevel level = this.getLevel();
SubLevelOccupancySavedData.getOrLoad(level).setDirty();
this.holdingChunkMap.queueDeletion(subLevel);
}
}

/**
* Moves loading-ticket ownership to a replacement sub-level in another container.
*/
@ApiStatus.Internal
public void transferTicketsTo(
final ServerSubLevel source,
final ServerSubLevelContainer destinationContainer,
final ServerSubLevel destination) {
final UUID uuid = source.getUniqueId();
final SubLevelTicketInfo info = this.allTickets.remove(uuid);
this.activeTickets.remove(source);

if (info == null) {
return;
}

info.setPointer(null);
destinationContainer.allTickets.put(uuid, info);
if (!info.tickets().isEmpty()) {
destinationContainer.activeTickets.put(destination, new ObjectArraySet<>(info.tickets()));
}

SubLevelTicketsSavedData.getOrLoad(this.getLevel()).setDirty();
SubLevelTicketsSavedData.getOrLoad(destinationContainer.getLevel()).setDirty();
}

@Override
protected SubLevel createSubLevel(final int globalPlotX, final int globalPlotZ, final Pose3d pose, final UUID uuid) {
final ServerLevel level = this.getLevel();
Expand Down Expand Up @@ -212,6 +238,40 @@ public <T> boolean addForceLoadTicket(final ServerSubLevel subLevel, final SubLe
return false;
}

/**
* Adds a force-loading ticket that only lasts for the current server session and is not written to saved data.
*/
public <T> boolean addTransientForceLoadTicket(
final ServerSubLevel subLevel,
final SubLevelLoadingTicketType<T> ticketType,
final T key) {
final SubLevelLoadingTicket<T> ticket = new SubLevelLoadingTicket<>(
ticketType, subLevel.getUniqueId(), key);
return this.activeTickets
.computeIfAbsent(subLevel, ignored -> new ObjectArraySet<>())
.add(ticket);
}

/**
* Removes a force-loading ticket previously added with {@link #addTransientForceLoadTicket}.
*/
public <T> boolean removeTransientForceLoadTicket(
final ServerSubLevel subLevel,
final SubLevelLoadingTicketType<T> ticketType,
final T key) {
final ObjectSet<SubLevelLoadingTicket<?>> tickets = this.activeTickets.get(subLevel);
if (tickets == null) {
return false;
}

final boolean removed = tickets.remove(new SubLevelLoadingTicket<>(
ticketType, subLevel.getUniqueId(), key));
if (tickets.isEmpty()) {
this.activeTickets.remove(subLevel);
}
return removed;
}

/**
* Removes a sub-level force-loading ticket
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,24 @@ public int getIndex(final int x, final int z) {
* @return the allocated plot
*/
public SubLevel allocateNewSubLevel(final Pose3d pose) {
return this.allocateNewSubLevel(UUID.randomUUID(), pose);
}

/**
* Allocates the first free plot for a sub-level with an existing persistent ID.
*
* @param uuid the persistent sub-level ID
* @param pose the initial pose
* @return the allocated sub-level
*/
public SubLevel allocateNewSubLevel(final UUID uuid, final Pose3d pose) {
final Vector2i firstEmptyPlot = this.getFirstEmptyPlot();

if (firstEmptyPlot == null) {
throw new IllegalStateException("No empty plots left in the plotgrid");
}

return this.allocateSubLevel(UUID.randomUUID(), firstEmptyPlot.x, firstEmptyPlot.y, pose);
return this.allocateSubLevel(uuid, firstEmptyPlot.x, firstEmptyPlot.y, pose);
}

/**
Expand Down Expand Up @@ -492,7 +503,7 @@ public void removeSubLevel(final int x, final int z, final SubLevelRemovalReason
this.allSubLevels.remove(subLevel);
this.subLevelsByUUID.remove(subLevel.getUniqueId());

if (reason == SubLevelRemovalReason.REMOVED) {
if (reason.clearsOccupancy()) {
this.getOccupancy().clear(index);
}
}
Expand Down Expand Up @@ -529,6 +540,14 @@ public void removeSubLevel(final SubLevel subLevel, final SubLevelRemovalReason
return this.subLevelsByUUID.get(uuid);
}

/**
* Notifies this container's observers that one sub-level instance replaced another.
*/
@ApiStatus.Internal
public void notifySubLevelTransferred(final SubLevel source, final SubLevel destination) {
this.observers.forEach(observer -> observer.onSubLevelTransferred(source, destination));
}

/**
* The occupancy of the plotgrid, including loaded and unloaded plots
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ default void onSubLevelAdded(final SubLevel subLevel) {
default void onSubLevelRemoved(final SubLevel subLevel, final SubLevelRemovalReason reason) {
}

/**
* Called after a server sub-level has moved between level containers.
* Implementations must replace references to {@code source} with {@code destination}.
*
* @param source the removed source instance
* @param destination the replacement instance in the destination level
*/
default void onSubLevelTransferred(final SubLevel source, final SubLevel destination) {
}

/**
* Called every tick for each {@link SubLevelContainer}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public void onSubLevelRemoved(final SubLevel subLevel, final SubLevelRemovalReas
if (info != null) {
info.setPointer(serverSubLevel.getLastSerializationPointer());
}
} else if (reason == SubLevelRemovalReason.REMOVED) {
} else if (reason == SubLevelRemovalReason.REMOVED || reason == SubLevelRemovalReason.TRANSFERRED) {
this.container.allTickets.remove(uuid);
this.container.activeTickets.remove(serverSubLevel);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.ryanhcode.sable.api.sublevel;

import dev.ryanhcode.sable.sublevel.ServerSubLevel;

import java.util.Map;
import java.util.UUID;

/**
* The replacement instances produced by a successful cross-level transfer.
* Persistent UUIDs are preserved, but runtime IDs and Java object identities change.
*
* @param root replacement for the requested root sub-level
* @param replacements all replacements indexed by persistent UUID
*/
public record SubLevelTransferResult(ServerSubLevel root, Map<UUID, ServerSubLevel> replacements) {
public SubLevelTransferResult {
replacements = Map.copyOf(replacements);
}
}
Loading