-
-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Modified root gradle project and `gradle.properties` in order to apply mappings and include minecraft only in projects that are modded. * Started writing server side of Figura, added bunch of packet classes. * Added interface for platform independent FriendlyByteBuf, so all the needed functions can be also used in Bukkit plugin * Added platform independent Identifier record, which is basically ResourceLocation, but both for Bukkit and Fabric/Forge. * Started writing AvatarManager for server side, gonna rewrite a lot tho. * Added a little Events system to make figura server side extendable but also secure enough. There are several types of events: Regular ones, cancellable, and returnable. Returnable events can be used for custom avatar/metadata/usedata loading implementation, for example from DB (by default everything including avatars, avatar metadata, and userdata, will be stored in a folder) From now i am going to take a big break from working on this, don't want to tell why but if short then i've got through something not good and just need some time. I will get back to working on this project as soon as i can.
- Loading branch information
Showing
42 changed files
with
2,006 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
repositories { | ||
|
||
} | ||
|
||
dependencies { | ||
implementation 'com.google.code.gson:gson:2.11.0' | ||
} |
77 changes: 77 additions & 0 deletions
77
server-common/src/main/java/org/figuramc/figura/server/FiguraServer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package org.figuramc.figura.server; | ||
|
||
import org.figuramc.figura.server.events.Events; | ||
import org.figuramc.figura.server.events.OutcomingPacketEvent; | ||
import org.figuramc.figura.server.packets.Packet; | ||
import org.figuramc.figura.server.packets.PacketHandler; | ||
import org.figuramc.figura.server.utils.Identifier; | ||
import org.figuramc.figura.server.utils.Utils; | ||
|
||
import java.nio.file.Path; | ||
import java.util.UUID; | ||
|
||
public abstract class FiguraServer { | ||
private static FiguraServer INSTANCE; | ||
private FiguraUserManager userManager; | ||
private FiguraServerConfig config; | ||
protected FiguraServer() { | ||
if (INSTANCE != null) throw new IllegalStateException("Can't create more than one instance of FiguraServer"); | ||
INSTANCE = this; | ||
} | ||
|
||
public static FiguraServer getInstance() { | ||
return INSTANCE; | ||
} | ||
|
||
public abstract Path getFiguraFolder(); | ||
|
||
public abstract void registerHandler(Identifier packetId, PacketHandler<?> handler); | ||
public abstract void unregisterHandler(Identifier packetId); | ||
|
||
public Path getUsersFolder() { | ||
return getFiguraFolder().resolve("users"); | ||
} | ||
|
||
public Path getAvatarsFolder() { | ||
return getFiguraFolder().resolve("avatars"); | ||
} | ||
|
||
public Path getAvatar(byte[] hash) { | ||
return getAvatarsFolder().resolve("%s.nbt".formatted(Utils.hexFromBytes(hash))); | ||
} | ||
|
||
public Path getAvatarMetadata(byte[] hash) { | ||
return getAvatarsFolder().resolve("%s.mtd".formatted(Utils.hexFromBytes(hash))); | ||
} | ||
|
||
public Path getUserdataFile(UUID user) { | ||
return getUsersFolder().resolve("%s.pl".formatted(Utils.uuidToHex(user))); | ||
} | ||
|
||
public final void init() { | ||
|
||
} | ||
|
||
public final void close() { | ||
|
||
INSTANCE = null; | ||
} | ||
|
||
public FiguraServerConfig config() { | ||
return config; | ||
} | ||
|
||
public FiguraUserManager userManager() { | ||
return userManager; | ||
} | ||
|
||
public final void sendPacket(UUID receiver, Packet packet) { | ||
OutcomingPacketEvent event = new OutcomingPacketEvent(receiver, packet); | ||
Events.call(event); | ||
if (!event.isCancelled()) { | ||
sendPacketInternal(receiver, packet); | ||
} | ||
} | ||
|
||
protected abstract void sendPacketInternal(UUID receiver, Packet packet); | ||
} |
47 changes: 47 additions & 0 deletions
47
server-common/src/main/java/org/figuramc/figura/server/FiguraServerConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package org.figuramc.figura.server; | ||
|
||
public final class FiguraServerConfig { | ||
private boolean pings = true; | ||
private boolean avatars = true; | ||
|
||
private int pingsRateLimit = 20; | ||
private int pingsSizeLimit = 10240; | ||
|
||
private int avatarSizeLimit = 102400; | ||
private int avatarsCountLimit = 1; | ||
|
||
private int garbageCollectionTicks = 1200; | ||
private int maxOutcomingChunkSize = 50000; | ||
|
||
public boolean pings() { | ||
return pings; | ||
} | ||
|
||
public boolean avatars() { | ||
return avatars; | ||
} | ||
|
||
public int pingsRateLimit() { | ||
return pingsRateLimit; | ||
} | ||
|
||
public int pingsSizeLimit() { | ||
return pingsSizeLimit; | ||
} | ||
|
||
public int avatarSizeLimit() { | ||
return avatarSizeLimit; | ||
} | ||
|
||
public int avatarsCountLimit() { | ||
return avatarsCountLimit; | ||
} | ||
|
||
public int garbageCollectionTicks() { | ||
return garbageCollectionTicks; | ||
} | ||
|
||
public int maxOutcomingChunkSize() { | ||
return maxOutcomingChunkSize; | ||
} | ||
} |
149 changes: 149 additions & 0 deletions
149
server-common/src/main/java/org/figuramc/figura/server/FiguraUser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package org.figuramc.figura.server; | ||
|
||
import org.figuramc.figura.server.packets.Packet; | ||
import org.figuramc.figura.server.packets.s2c.S2CUserdataPacket; | ||
import org.figuramc.figura.server.utils.IFriendlyByteBuf; | ||
import org.figuramc.figura.server.utils.InputStreamByteBuf; | ||
import org.figuramc.figura.server.utils.OutputStreamByteBuf; | ||
|
||
import java.io.*; | ||
import java.nio.file.Path; | ||
import java.util.*; | ||
|
||
import static java.nio.charset.StandardCharsets.UTF_8; | ||
|
||
public final class FiguraUser { | ||
private final UUID player; | ||
private boolean avatars; | ||
private boolean pings; | ||
private final PingCounter pingCounter = new PingCounter(); | ||
private final BitSet prideBadges; | ||
private final HashMap<String, byte[]> equippedAvatars; | ||
|
||
private final HashMap<String, byte[]> ownedAvatars; | ||
|
||
public FiguraUser(UUID player, boolean allowAvatars, boolean allowPings, BitSet prideBadges, HashMap<String, byte[]> equippedAvatars, HashMap<String, byte[]> ownedAvatars) { | ||
this.player = player; | ||
this.avatars = allowAvatars; | ||
this.pings = allowPings; | ||
this.prideBadges = prideBadges; | ||
this.equippedAvatars = equippedAvatars; | ||
this.ownedAvatars = ownedAvatars; | ||
} | ||
|
||
public UUID player() { | ||
return player; | ||
} | ||
|
||
public boolean avatarsAllowed() { | ||
return avatars; | ||
} | ||
|
||
public boolean pingsAllowed() { | ||
return pings; | ||
} | ||
|
||
public PingCounter pingCounter() { | ||
return pingCounter; | ||
} | ||
|
||
public BitSet prideBadges() { | ||
return prideBadges; | ||
} | ||
|
||
public HashMap<String, byte[]> equippedAvatars() { | ||
return equippedAvatars; | ||
} | ||
|
||
public HashMap<String, byte[]> ownedAvatars() { | ||
return ownedAvatars; | ||
} | ||
|
||
public void sendPacket(Packet packet) { | ||
FiguraServer.getInstance().sendPacket(player, packet); | ||
} | ||
|
||
public void save(Path file) { | ||
file.getParent().toFile().mkdirs(); | ||
File playerFile = file.toFile(); | ||
try { | ||
FileOutputStream fos = new FileOutputStream(playerFile); | ||
OutputStreamByteBuf buf = new OutputStreamByteBuf(fos); | ||
save(buf); | ||
fos.close(); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
public void save(IFriendlyByteBuf buf) { | ||
buf.writeBytes(prideBadges.toByteArray()); | ||
buf.writeVarInt(equippedAvatars.size()); | ||
for (var equippedAvatar : equippedAvatars.entrySet()) { | ||
buf.writeByteArray(equippedAvatar.getKey().getBytes(UTF_8)); | ||
buf.writeBytes(equippedAvatar.getValue()); | ||
} | ||
buf.writeVarInt(ownedAvatars.size()); | ||
for (var ownedAvatar : ownedAvatars.entrySet()) { | ||
buf.writeByteArray(ownedAvatar.getKey().getBytes(UTF_8)); | ||
buf.writeBytes(ownedAvatar.getValue()); | ||
} | ||
} | ||
|
||
public static FiguraUser load(UUID player, boolean pings, boolean avatars, Path playerFile) { | ||
try (FileInputStream fis = new FileInputStream(playerFile.toFile())) { | ||
InputStreamByteBuf buf = new InputStreamByteBuf(fis); | ||
return load(player, pings, avatars, buf); | ||
} catch (FileNotFoundException e) { | ||
return new FiguraUser(player, avatars, pings, new BitSet(), new HashMap<>(), new HashMap<>()); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
public static FiguraUser load(UUID player, boolean pings, boolean avatars, IFriendlyByteBuf buf) { | ||
BitSet prideBadges = BitSet.valueOf(buf.readByteArray(256)); | ||
int equippedAvatarsCount = buf.readVarInt(); | ||
HashMap<String, byte[]> equippedAvatars = new HashMap<>(); | ||
for (int i = 0; i < equippedAvatarsCount; i++) { | ||
String id = new String(buf.readByteArray(256), UTF_8); | ||
byte[] hash = buf.readHash(); | ||
equippedAvatars.put(id, hash); | ||
} | ||
HashMap<String, byte[]> ownedAvatars = new HashMap<>(); | ||
int ownedAvatarsCount = buf.readVarInt(); | ||
for (int i = 0; i < ownedAvatarsCount; i++) { | ||
String id = new String(buf.readByteArray(256), UTF_8); | ||
byte[] hash = buf.readHash(); | ||
ownedAvatars.put(id, hash); | ||
} | ||
return new FiguraUser(player, avatars, pings, prideBadges, equippedAvatars, ownedAvatars); | ||
} | ||
|
||
public byte[] findEHash(byte[] hash) { | ||
return null; | ||
} | ||
|
||
private static class PingCounter { | ||
private int bytesSent; // Amount of total bytes sent in last 20 ticks | ||
private int pingsSent; // Amount of pings sent in last 20 ticks | ||
|
||
public int bytesSent() { | ||
return bytesSent; | ||
} | ||
|
||
public int pingsSent() { | ||
return pingsSent; | ||
} | ||
|
||
public void addPing(int size) { | ||
pingsSent++; | ||
bytesSent += size; | ||
} | ||
|
||
public void reset() { | ||
bytesSent = 0; | ||
pingsSent = 0; | ||
} | ||
} | ||
} |
58 changes: 58 additions & 0 deletions
58
server-common/src/main/java/org/figuramc/figura/server/FiguraUserManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package org.figuramc.figura.server; | ||
|
||
import org.figuramc.figura.server.events.Events; | ||
import org.figuramc.figura.server.events.LoadPlayerDataEvent; | ||
import org.figuramc.figura.server.packets.s2c.S2CBackendHandshakePacket; | ||
|
||
import java.nio.file.Path; | ||
import java.util.HashMap; | ||
import java.util.UUID; | ||
|
||
public final class FiguraUserManager { | ||
private final FiguraServer parent; | ||
private final HashMap<UUID, FiguraUser> users = new HashMap<>(); | ||
|
||
public FiguraUserManager(FiguraServer parent) { | ||
this.parent = parent; | ||
} | ||
|
||
public FiguraUser getUser(UUID playerUUID) { | ||
return users.get(playerUUID); | ||
} | ||
|
||
public void onPlayerJoin(UUID player) { | ||
parent.sendPacket(player, new S2CBackendHandshakePacket( | ||
parent.config().pings(), | ||
parent.config().pingsRateLimit(), | ||
parent.config().pingsSizeLimit(), | ||
parent.config().avatars(), | ||
parent.config().avatarSizeLimit(), | ||
parent.config().avatarsCountLimit() | ||
)); | ||
} | ||
|
||
public FiguraUser authorisePlayer(UUID player, boolean allowPings, boolean allowAvatars) { | ||
return users.computeIfAbsent(player, (k) -> loadPlayerData(k, allowPings, allowAvatars)); | ||
} | ||
|
||
private FiguraUser loadPlayerData(UUID player, boolean allowPings, boolean allowAvatars) { | ||
LoadPlayerDataEvent playerDataEvent = Events.call(new LoadPlayerDataEvent(player)); | ||
if (playerDataEvent.returned()) return playerDataEvent.returnValue(); | ||
Path dataFile = parent.getUserdataFile(player); | ||
return FiguraUser.load(player, allowPings, allowAvatars, dataFile); | ||
} | ||
|
||
public void onPlayerLeave(UUID player) { | ||
users.computeIfPresent(player, (uuid, pl) -> { | ||
pl.save(parent.getUserdataFile(pl.player())); | ||
return null; | ||
}); | ||
} | ||
|
||
public void close() { | ||
for (FiguraUser pl: users.values()) { | ||
pl.save(parent.getUserdataFile(pl.player())); | ||
} | ||
users.clear(); | ||
} | ||
} |
Oops, something went wrong.