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
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,37 @@

package eu.cloudnetservice.modules.signs.configuration;

import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.List;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

@ToString
@EqualsAndHashCode
public class SignLayoutsHolder {

private static final VarHandle LAST_UPDATE_TICK;

static {
try {
var lookup = MethodHandles.lookup();
LAST_UPDATE_TICK = lookup
.findVarHandle(SignLayoutsHolder.class, "lastUpdateTick", long.class)
.withInvokeExactBehavior();
} catch (NoSuchFieldException | IllegalAccessException exception) {
throw new ExceptionInInitializerError(exception);
}
}

private final int animationsPerSecond;
private final List<SignLayout> signLayouts;

private transient boolean tickBlocked;
private transient int currentAnimation = -1;
@SuppressWarnings("unused") // accessed/changed by LAST_UPDATE_TICK
private transient long lastUpdateTick;

public SignLayoutsHolder(int animationsPerSecond, @NonNull List<SignLayout> signLayouts) {
this.animationsPerSecond = animationsPerSecond;
Expand All @@ -52,33 +69,28 @@ public boolean hasLayouts() {
return !this.signLayouts.isEmpty();
}

public boolean tickBlocked() {
return this.tickBlocked;
}

public void enableTickBlock() {
this.tickBlocked = true;
}

public @NonNull SignLayoutsHolder releaseTickBlock() {
this.tickBlocked = false;
return this;
}

public @NonNull SignLayout currentLayout() {
return this.signLayouts().get(this.currentAnimation());
}

public @NonNull SignLayoutsHolder tick() {
if (!this.tickBlocked()) {
if (++this.currentAnimation >= this.signLayouts.size()) {
this.currentAnimation = 0;
/* == apis only accessed by platforms, not for external use == */

@ApiStatus.Internal
public @Nullable SignLayout currentLayout(int tps) {
var layouts = this.signLayouts;
var layoutEntryCount = layouts.size();
return switch (layoutEntryCount) {
case 0 -> null;
case 1 -> layouts.getFirst();
default -> {
var lastUpdateTick = (long) LAST_UPDATE_TICK.getAcquire(this);
var animationsPerSecond = Math.min(tps, this.animationsPerSecond); // cannot display more animations per second
var animationIntervalTicks = tps / animationsPerSecond;
var steps = lastUpdateTick / animationIntervalTicks;
var layoutIndex = (int) (steps % layoutEntryCount);
yield layouts.get(layoutIndex);
}
}
return this;
};
}

public int currentAnimation() {
return Math.max(0, this.currentAnimation);
@ApiStatus.Internal
public void tick(long currentTick) {
LAST_UPDATE_TICK.setRelease(this, currentTick);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,17 @@
import eu.cloudnetservice.modules.bridge.WorldPosition;
import eu.cloudnetservice.modules.signs.Sign;
import eu.cloudnetservice.modules.signs.configuration.SignConfigurationEntry;
import eu.cloudnetservice.modules.signs.configuration.SignLayoutsHolder;
import eu.cloudnetservice.modules.signs.configuration.SignLayout;
import eu.cloudnetservice.modules.signs.configuration.SignsConfiguration;
import eu.cloudnetservice.modules.signs.impl.AbstractSignManagement;
import eu.cloudnetservice.modules.signs.impl.SharedChannelMessageListener;
import eu.cloudnetservice.modules.signs.impl.util.LayoutUtil;
import eu.cloudnetservice.modules.signs.impl.util.PriorityUtil;
import eu.cloudnetservice.wrapper.configuration.WrapperConfiguration;
import io.vavr.Tuple2;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
Expand Down Expand Up @@ -76,7 +77,7 @@ public abstract class PlatformSignManagement<P, L, C> extends AbstractSignManage
protected final Map<WorldPosition, PlatformSign<P, C>> platformSigns = new ConcurrentHashMap<>();
protected final Queue<ServiceInfoSnapshot> waitingAssignments = new ConcurrentLinkedQueue<>();

protected int currentTick;
protected long currentTick;

protected PlatformSignManagement(
@NonNull EventManager eventManager,
Expand Down Expand Up @@ -246,10 +247,6 @@ public void handleServiceRemove(@NonNull ServiceInfoSnapshot snapshot) {
}

public void initialize() {
this.initialize(new HashMap<>());
}

public void initialize(@NonNull Map<SignLayoutsHolder, Set<PlatformSign<P, C>>> signsNeedingTicking) {
if (this.signsConfiguration != null) {
// initialize the platform signs
for (var value : this.signs.values()) {
Expand All @@ -259,7 +256,7 @@ public void initialize(@NonNull Map<SignLayoutsHolder, Set<PlatformSign<P, C>>>
// start the needed tasks
this.executorService.scheduleWithFixedDelay(() -> {
try {
this.tick(signsNeedingTicking);
this.tick();
} catch (Throwable throwable) {
LOGGER.error("Exception ticking signs", throwable);
}
Expand Down Expand Up @@ -318,67 +315,54 @@ protected boolean checkTemplatePath(@NonNull ServiceInfoSnapshot snapshot, @NonN
}

@ApiStatus.Internal
protected void tick(@NonNull Map<SignLayoutsHolder, Set<PlatformSign<P, C>>> signsNeedingTicking) {
protected void tick() {
this.currentTick++;

var ownEntry = this.applicableSignConfigurationEntry();
if (ownEntry != null) {
// marker if there are any updates we need to do - if there are no updates there is no need to schedule them
// which saves server resources
var hasUpdates = false;
for (var value : this.platformSigns.values()) {
// tick all sign layouts which we need to tick in the current tick
var holder = LayoutUtil.layoutHolder(ownEntry, value.base(), value.currentTarget());
if (holder.hasLayouts() && holder.animationsPerSecond() > 0
&& this.currentTick % (this.tps() / holder.animationsPerSecond()) == 0) {
// tick the holder, then block the tick
holder.tick().enableTickBlock();
// register the sign for updates if we need to
if (value.needsUpdates()) {
hasUpdates = true;
signsNeedingTicking.computeIfAbsent(holder, $ -> new HashSet<>()).add(value);
List<Tuple2<SignLayout, PlatformSign<P, C>>> signsToTick = null;
for (var sign : this.platformSigns.values()) {
// update all sign layouts that need an animation tick,
// also register the sign for an update if the associated layout changed
var holder = LayoutUtil.layoutHolder(ownEntry, sign.base(), sign.currentTarget());
if (holder.hasLayouts() && holder.animationsPerSecond() > 0) {
var animationIntervalTicks = this.tps() / holder.animationsPerSecond();
if (animationIntervalTicks == 0 || this.currentTick % animationIntervalTicks == 0) {
holder.tick(this.currentTick);
var nextLayout = holder.currentLayout(this.tps());
if (nextLayout != null && sign.needsUpdates()) {
// the sign is loaded and needs an update to display the new layout
if (signsToTick == null) {
signsToTick = new ArrayList<>();
}
signsToTick.add(new Tuple2<>(nextLayout, sign));
}
}
}
}

// execute updates if there are any
if (hasUpdates) {
if (signsToTick != null) {
var finalSignsToTick = signsToTick; // must not be modified anymore
this.mainThreadExecutor.execute(() -> {
for (var entry : signsNeedingTicking.entrySet()) {
var layout = entry.getKey().releaseTickBlock().currentLayout();
// push out all sign changes we recorded previously
// we need to copy all entries of the set into a new array in case we have a thread de-sync (for example async
// tick but sync update) as we need to clear the underlying set after the call to prevent double ticks
var iterator = entry.getValue().iterator();
while (iterator.hasNext()) {
// update the sign, at this point the sign must be loaded - we can just push the change and unregister it
iterator.next().updateSign(layout);
iterator.remove();
}
for (var layoutSignTuple : finalSignsToTick) {
var layout = layoutSignTuple._1();
var sign = layoutSignTuple._2();
sign.updateSign(layout);
}
});
}

// check if we have waiting services which are not yet assigned - try to assign them to a sign
if (!this.waitingAssignments.isEmpty()) {
for (var waitingAssignment : this.waitingAssignments) {
// get the next free sign to which can assign the service
var freeSign = this.nextFreeSign(waitingAssignment);
if (freeSign != null) {
// remove instantly
this.waitingAssignments.remove(waitingAssignment);
// assign the service to the sign, the layout of it will be updated within the next second
// we could directly update the layout but there is no need to do that
freeSign.currentTarget(waitingAssignment);
}
}
}
}

// reset the tick counter if we reached the max tps
if (this.currentTick >= this.tps()) {
this.currentTick = 0;
}
}

protected @Nullable PlatformSign<P, C> nextFreeSign(@NonNull ServiceInfoSnapshot snapshot) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,13 @@
import eu.cloudnetservice.modules.bridge.WorldPosition;
import eu.cloudnetservice.modules.signs.Sign;
import eu.cloudnetservice.modules.signs.SignManagement;
import eu.cloudnetservice.modules.signs.configuration.SignLayoutsHolder;
import eu.cloudnetservice.modules.signs.impl.InternalSignManagement;
import eu.cloudnetservice.modules.signs.impl.platform.PlatformSign;
import eu.cloudnetservice.modules.signs.impl.platform.PlatformSignManagement;
import eu.cloudnetservice.wrapper.configuration.WrapperConfiguration;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import lombok.NonNull;
Expand Down Expand Up @@ -133,10 +130,8 @@ protected void startKnockbackTask() {
}

@Override
protected void tick(@NonNull Map<SignLayoutsHolder, Set<PlatformSign<ServerPlayer, Component>>> signsNeedingTicking) {
this.mainThreadExecutor.execute(() -> {
super.tick(signsNeedingTicking);
});
protected void tick() {
this.mainThreadExecutor.execute(super::tick);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,6 @@ private LayoutUtil() {
throw new UnsupportedOperationException();
}

public static @NonNull SignLayout layout(
@NonNull SignConfigurationEntry entry,
@NonNull Sign sign,
@Nullable ServiceInfoSnapshot snapshot
) {
return layoutHolder(entry, sign, snapshot).currentLayout();
}

public static @NonNull SignLayout layoutAndTick(
@NonNull SignConfigurationEntry entry,
@NonNull Sign sign,
@Nullable ServiceInfoSnapshot snapshot
) {
return layoutHolder(entry, sign, snapshot).tick().currentLayout();
}

public static boolean switchToSearching(
@NonNull ServiceInfoSnapshot snapshot,
@Nullable SignConfigurationEntry entry
Expand Down
Loading