Negotiate protocol version and refactor BetterWhitelistServer
ci/woodpecker/push/jfmod Pipeline was successful
Details
ci/woodpecker/push/jfmod Pipeline was successful
Details
parent
36fae30cf1
commit
6ccd71aac6
@ -0,0 +1,17 @@
|
||||
package io.gitlab.jfronny.betterwhitelist.server;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
|
||||
class Challenge {
|
||||
public final ManualFuture<?> challengeCompleted = new ManualFuture<>();
|
||||
public final ManualFuture<Dynamic<?>> response = new ManualFuture<>();
|
||||
public final GameProfile profile;
|
||||
public PacketSender sender;
|
||||
|
||||
public Challenge(GameProfile profile, PacketSender sender) {
|
||||
this.profile = profile;
|
||||
this.sender = sender;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package io.gitlab.jfronny.betterwhitelist.server;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ManualFuture<T> implements Future<T> {
|
||||
private Result state = Result.RUNNING;
|
||||
private T result;
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean b) {
|
||||
if (state != Result.RUNNING) return false;
|
||||
cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return state == Result.CANCELLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return state == Result.DONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() throws InterruptedException, ExecutionException {
|
||||
while (state == Result.RUNNING) Thread.sleep(10);
|
||||
if (state == Result.CANCELLED) throw new CancellationException();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long l, @NotNull TimeUnit timeUnit) throws TimeoutException {
|
||||
long millis = timeUnit.toMillis(l) / 10;
|
||||
while (state == Result.RUNNING && millis-- > 0) {
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
if (millis <= 0) throw new TimeoutException();
|
||||
if (state == Result.CANCELLED) throw new CancellationException();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
this.state = Result.CANCELLED;
|
||||
}
|
||||
|
||||
public void complete(T result) {
|
||||
if (state != Result.RUNNING) throw new IllegalStateException("Attempted to complete non-running future");
|
||||
this.state = Result.DONE;
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.state = Result.RUNNING;
|
||||
}
|
||||
|
||||
private enum Result {
|
||||
RUNNING, CANCELLED, DONE
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
package io.gitlab.jfronny.betterwhitelist.server;
|
||||
|
||||
import io.gitlab.jfronny.betterwhitelist.BetterWhitelist;
|
||||
import io.gitlab.jfronny.muscript.data.Scope;
|
||||
import io.gitlab.jfronny.muscript.data.Script;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.DNull;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.Dynamic;
|
||||
import io.gitlab.jfronny.muscript.data.dynamic.additional.DFinal;
|
||||
import io.gitlab.jfronny.muscript.error.LocationalException;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class ServerScope {
|
||||
public static Dynamic<?> run(Script script, Challenge challenge) {
|
||||
try {
|
||||
return script.run(fork(challenge));
|
||||
} catch (LocationalException le) {
|
||||
for (Throwable t = le; t != null; t = t.getCause()) {
|
||||
if (t instanceof ServerScope.AssertFail af) throw af;
|
||||
}
|
||||
throw le;
|
||||
}
|
||||
}
|
||||
|
||||
private static Scope fork(Challenge challenge) {
|
||||
return BetterWhitelist.SCOPE.fork().set("assert", args -> {
|
||||
if (args.size() != 1 && args.size() != 2) throw new IllegalArgumentException("Invalid number of arguments for assert: expected 1 or 2 but got " + args.size());
|
||||
if (!args.get(0).asBool().getValue()) throw new AssertFail(args.size() > 1 ? args.get(1).asString().getValue() : "Failed Whitelist Check");
|
||||
return new DNull();
|
||||
}).set("challenge", args -> {
|
||||
if (args.size() == 0) throw new IllegalArgumentException("Invalid number of arguments for challenge: expected 1 or more but got 0");
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
String challengeString = Dynamic.serialize(args.get(0).asCallable());
|
||||
BetterWhitelist.LOG.info("Sending challenge to " + challenge.profile.getName() + ": " + challengeString);
|
||||
buf.writeString(challengeString);
|
||||
List<Dynamic<?>> params = args.getValue().subList(1, args.size());
|
||||
buf.writeInt(params.size());
|
||||
params.forEach(p -> buf.writeString(Dynamic.serialize(p)));
|
||||
challenge.response.reset();
|
||||
challenge.sender.sendPacket(BetterWhitelist.CHALLENGE_CHANNEL, buf);
|
||||
try {
|
||||
return challenge.response.get(1, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException e) {
|
||||
throw new AssertFail("Took too long to respond");
|
||||
}
|
||||
}).set("user", Map.of(
|
||||
"id", challenge.profile.getId() == null ? new DNull() : DFinal.of(challenge.profile.getId().toString()),
|
||||
"name", DFinal.of(challenge.profile.getName())
|
||||
));
|
||||
}
|
||||
|
||||
public static class AssertFail extends RuntimeException {
|
||||
public AssertFail(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue