Implemented Bossbar & Sounds Partially

This commit is contained in:
LOOHP 2022-12-08 02:35:37 +00:00
parent 8dd92345d4
commit 9193d907d1
18 changed files with 919 additions and 227 deletions

12
pom.xml
View File

@ -24,7 +24,7 @@
<groupId>com.loohp</groupId>
<artifactId>Limbo</artifactId>
<name>Limbo</name>
<version>0.6.19-ALPHA</version>
<version>0.6.20-ALPHA</version>
<description>Standalone Limbo Minecraft Server.</description>
<url>https://github.com/LOOHP/Limbo</url>
@ -256,31 +256,31 @@
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-gson</artifactId>
<version>4.10.1</version>
<version>4.12.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-legacy</artifactId>
<version>4.10.1</version>
<version>4.12.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-plain</artifactId>
<version>4.10.1</version>
<version>4.12.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-api</artifactId>
<version>4.10.1</version>
<version>4.12.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-nbt</artifactId>
<version>4.10.1</version>
<version>4.12.0</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@ -19,42 +19,9 @@
package com.loohp.limbo;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.loohp.limbo.bossbar.KeyedBossBar;
import com.loohp.limbo.commands.CommandSender;
import com.loohp.limbo.commands.DefaultCommands;
import com.loohp.limbo.consolegui.GUI;
@ -66,6 +33,7 @@ import com.loohp.limbo.network.ServerConnection;
import com.loohp.limbo.network.protocol.packets.Packet;
import com.loohp.limbo.network.protocol.packets.PacketIn;
import com.loohp.limbo.network.protocol.packets.PacketOut;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutBoss;
import com.loohp.limbo.permissions.PermissionsManager;
import com.loohp.limbo.player.Player;
import com.loohp.limbo.plugins.LimboPlugin;
@ -74,21 +42,56 @@ import com.loohp.limbo.scheduler.LimboScheduler;
import com.loohp.limbo.scheduler.Tick;
import com.loohp.limbo.utils.CustomStringUtils;
import com.loohp.limbo.utils.ImageUtils;
import com.loohp.limbo.utils.NamespacedKey;
import com.loohp.limbo.utils.NetworkUtils;
import com.loohp.limbo.world.DimensionRegistry;
import com.loohp.limbo.world.Environment;
import com.loohp.limbo.world.Schematic;
import com.loohp.limbo.world.World;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.querz.nbt.io.NBTUtil;
import net.querz.nbt.tag.CompoundTag;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import javax.swing.UnsupportedLookAndFeelException;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
public class Limbo {
public final class Limbo {
public static final String LIMBO_BRAND = "Limbo";
@ -135,39 +138,41 @@ public class Limbo {
public final int SERVER_IMPLEMENTATION_PROTOCOL = 761;
public final String LIMBO_IMPLEMENTATION_VERSION;
private AtomicBoolean isRunning;
private final AtomicBoolean isRunning;
private ServerConnection server;
private Console console;
private final ServerConnection server;
private final Console console;
private List<World> worlds = new ArrayList<>();
private Map<String, Player> playersByName = new HashMap<>();
private Map<UUID, Player> playersByUUID = new HashMap<>();
private final List<World> worlds = new CopyOnWriteArrayList<>();
final Map<String, Player> playersByName = new ConcurrentHashMap<>();
final Map<UUID, Player> playersByUUID = new ConcurrentHashMap<>();
private final Map<NamespacedKey, KeyedBossBar> bossBars = new ConcurrentHashMap<>();
private ServerProperties properties;
private final ServerProperties properties;
private PluginManager pluginManager;
private EventsManager eventsManager;
private PermissionsManager permissionManager;
private File pluginFolder;
private final PluginManager pluginManager;
private final EventsManager eventsManager;
private final PermissionsManager permissionManager;
private final File pluginFolder;
private File internalDataFolder;
private final File internalDataFolder;
private DimensionRegistry dimensionRegistry;
private final DimensionRegistry dimensionRegistry;
private Tick tick;
private LimboScheduler scheduler;
private final Tick tick;
private final LimboScheduler scheduler;
private Metrics metrics;
private final Metrics metrics;
public AtomicInteger entityIdCount = new AtomicInteger();
public final AtomicInteger entityIdCount = new AtomicInteger();
@SuppressWarnings("deprecation")
private Unsafe unsafe = new Unsafe();
private Unsafe unsafe;
@SuppressWarnings("unchecked")
public Limbo() throws IOException, ParseException, NumberFormatException, ClassNotFoundException, InterruptedException {
instance = this;
unsafe = new Unsafe(this);
isRunning = new AtomicBoolean(true);
if (!noGui) {
@ -305,7 +310,7 @@ public class Limbo {
e.printStackTrace();
}
}
scheduler = new LimboScheduler();
tick = new Tick(this);
@ -428,7 +433,31 @@ public class Limbo {
worlds.remove(world);
}
}
public KeyedBossBar createBossBar(NamespacedKey namespacedKey, Component name, float progress, BossBar.Color color, BossBar.Overlay overlay, BossBar.Flag... flags) {
KeyedBossBar keyedBossBar = new KeyedBossBar(namespacedKey, BossBar.bossBar(name, progress, color, overlay, new HashSet<>(Arrays.asList(flags))));
bossBars.put(namespacedKey, keyedBossBar);
return keyedBossBar;
}
public void removeBossBar(NamespacedKey namespacedKey) {
KeyedBossBar keyedBossBar = bossBars.remove(namespacedKey);
keyedBossBar.getProperties().removeListener(keyedBossBar.getUnsafe().getLimboListener());
keyedBossBar.getUnsafe().invalidate();
PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(keyedBossBar, PacketPlayOutBoss.BossBarAction.REMOVE);
for (Player player : keyedBossBar.getPlayers()) {
try {
player.clientConnection.sendPacket(packetPlayOutBoss);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public Map<NamespacedKey, KeyedBossBar> getBossBars() {
return Collections.unmodifiableMap(bossBars);
}
public ServerProperties getServerProperties() {
return properties;
}
@ -457,16 +486,6 @@ public class Limbo {
return playersByUUID.get(uuid);
}
public void addPlayer(Player player) {
playersByName.put(player.getName(), player);
playersByUUID.put(player.getUniqueId(), player);
}
public void removePlayer(Player player) {
playersByName.remove(player.getName());
playersByUUID.remove(player.getUniqueId());
}
public List<World> getWorlds() {
return new ArrayList<>(worlds);
}

View File

@ -28,13 +28,16 @@ import com.loohp.limbo.player.Player;
import com.loohp.limbo.utils.GameMode;
import com.loohp.limbo.world.World;
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public class Unsafe {
private final Limbo instance;
private com.loohp.limbo.player.Unsafe playerUnsafe;
private com.loohp.limbo.world.Unsafe worldUnsafe;
protected Unsafe() {
protected Unsafe(Limbo instance) {
this.instance = instance;
try {
Constructor<com.loohp.limbo.player.Unsafe> playerConstructor = com.loohp.limbo.player.Unsafe.class.getDeclaredConstructor();
playerConstructor.setAccessible(true);
@ -78,4 +81,17 @@ public class Unsafe {
playerUnsafe.a(player, location);
}
@Deprecated
public void addPlayer(Player player) {
instance.playersByName.put(player.getName(), player);
instance.playersByUUID.put(player.getUniqueId(), player);
}
@Deprecated
public void removePlayer(Player player) {
instance.getBossBars().values().forEach(each -> each.hidePlayer(player));
instance.playersByName.remove(player.getName());
instance.playersByUUID.remove(player.getUniqueId());
}
}

View File

@ -0,0 +1,176 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.bossbar;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutBoss;
import com.loohp.limbo.player.Player;
import com.loohp.limbo.utils.NamespacedKey;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class KeyedBossBar {
private final UUID uuid;
private final NamespacedKey key;
private final BossBar properties;
private final Set<Player> players;
protected final LimboBossBarHandler listener;
protected final AtomicBoolean valid;
private final Unsafe unsafe;
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public KeyedBossBar(NamespacedKey key, BossBar properties) {
this.uuid = UUID.randomUUID();
this.key = key;
this.properties = properties;
this.players = ConcurrentHashMap.newKeySet();
this.listener = new LimboBossBarHandler(this);
this.properties.addListener(listener);
this.valid = new AtomicBoolean(true);
this.unsafe = new Unsafe(this);
}
public UUID getUniqueId() {
return uuid;
}
public NamespacedKey getKey() {
return key;
}
public BossBar getProperties() {
return properties;
}
public Set<Player> getPlayers() {
return Collections.unmodifiableSet(players);
}
public UUID getUuid() {
return uuid;
}
public boolean isValid() {
return valid.get();
}
@Deprecated
public Unsafe getUnsafe() {
return unsafe;
}
public boolean showPlayer(Player player) {
PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(this, PacketPlayOutBoss.BossBarAction.ADD);
try {
player.clientConnection.sendPacket(packetPlayOutBoss);
} catch (IOException ignore) {
}
return players.add(player);
}
public boolean hidePlayer(Player player) {
PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(this, PacketPlayOutBoss.BossBarAction.REMOVE);
try {
player.clientConnection.sendPacket(packetPlayOutBoss);
} catch (IOException ignore) {
}
return players.remove(player);
}
public static class LimboBossBarHandler implements BossBar.Listener {
private final KeyedBossBar parent;
private LimboBossBarHandler(KeyedBossBar parent) {
this.parent = parent;
}
@Override
public void bossBarNameChanged(@NotNull BossBar bar, @NotNull Component oldName, @NotNull Component newName) {
PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(parent, PacketPlayOutBoss.BossBarAction.UPDATE_NAME);
for (Player player : parent.getPlayers()) {
try {
player.clientConnection.sendPacket(packetPlayOutBoss);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void bossBarProgressChanged(@NotNull BossBar bar, float oldProgress, float newProgress) {
PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(parent, PacketPlayOutBoss.BossBarAction.UPDATE_PROGRESS);
for (Player player : parent.getPlayers()) {
try {
player.clientConnection.sendPacket(packetPlayOutBoss);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void bossBarColorChanged(@NotNull BossBar bar, BossBar.@NotNull Color oldColor, BossBar.@NotNull Color newColor) {
PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(parent, PacketPlayOutBoss.BossBarAction.UPDATE_STYLE);
for (Player player : parent.getPlayers()) {
try {
player.clientConnection.sendPacket(packetPlayOutBoss);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void bossBarOverlayChanged(@NotNull BossBar bar, BossBar.@NotNull Overlay oldOverlay, BossBar.@NotNull Overlay newOverlay) {
PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(parent, PacketPlayOutBoss.BossBarAction.UPDATE_STYLE);
for (Player player : parent.getPlayers()) {
try {
player.clientConnection.sendPacket(packetPlayOutBoss);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void bossBarFlagsChanged(@NotNull BossBar bar, @NotNull Set<BossBar.Flag> flagsAdded, @NotNull Set<BossBar.Flag> flagsRemoved) {
PacketPlayOutBoss packetPlayOutBoss = new PacketPlayOutBoss(parent, PacketPlayOutBoss.BossBarAction.UPDATE_PROPERTIES);
for (Player player : parent.getPlayers()) {
try {
player.clientConnection.sendPacket(packetPlayOutBoss);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,42 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.bossbar;
@SuppressWarnings("DeprecatedIsStillUsed")
@Deprecated
public class Unsafe {
private final KeyedBossBar instance;
protected Unsafe(KeyedBossBar instance) {
this.instance = instance;
}
@Deprecated
public KeyedBossBar.LimboBossBarHandler getLimboListener() {
return instance.listener;
}
@Deprecated
public void invalidate() {
instance.valid.set(false);
}
}

View File

@ -34,46 +34,46 @@ import net.md_5.bungee.api.chat.BaseComponent;
public interface CommandSender extends Audience {
public void sendMessage(BaseComponent[] component, UUID uuid);
void sendMessage(BaseComponent[] component, UUID uuid);
public void sendMessage(BaseComponent component, UUID uuid);
void sendMessage(BaseComponent component, UUID uuid);
public void sendMessage(String message, UUID uuid);
void sendMessage(String message, UUID uuid);
public void sendMessage(BaseComponent[] component);
void sendMessage(BaseComponent[] component);
public void sendMessage(BaseComponent component);
void sendMessage(BaseComponent component);
public void sendMessage(String message);
void sendMessage(String message);
public boolean hasPermission(String permission);
boolean hasPermission(String permission);
public String getName();
public void sendMessage(Identity source, Component message, MessageType type);
public void openBook(Book book);
public void stopSound(SoundStop stop);
public void playSound(Sound sound, Sound.Emitter emitter);
public void playSound(Sound sound, double x, double y, double z);
public void playSound(Sound sound);
public void sendActionBar(Component message);
public void sendPlayerListHeaderAndFooter(Component header, Component footer);
public <T> void sendTitlePart(TitlePart<T> part, T value);
public void clearTitle();
public void resetTitle();
public void showBossBar(BossBar bar);
String getName();
public void hideBossBar(BossBar bar);
void sendMessage(Identity source, Component message, MessageType type);
void openBook(Book book);
void stopSound(SoundStop stop);
void playSound(Sound sound, Sound.Emitter emitter);
void playSound(Sound sound, double x, double y, double z);
void playSound(Sound sound);
void sendActionBar(Component message);
void sendPlayerListHeaderAndFooter(Component header, Component footer);
<T> void sendTitlePart(TitlePart<T> part, T value);
void clearTitle();
void resetTitle();
void showBossBar(BossBar bar);
void hideBossBar(BossBar bar);
}

View File

@ -28,11 +28,12 @@ import com.loohp.limbo.location.Location;
import com.loohp.limbo.utils.BungeecordAdventureConversionUtils;
import com.loohp.limbo.world.World;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.md_5.bungee.api.chat.BaseComponent;
public abstract class Entity {
public abstract class Entity implements Sound.Emitter {
@WatchableField(MetadataIndex = 0, WatchableObjectType = WatchableObjectType.BYTE, IsBitmask = true, Bitmask = 0x01)
protected boolean onFire = false;

View File

@ -48,7 +48,7 @@ public class Channel implements AutoCloseable {
private void ensureOpen() {
if (!valid.get()) {
throw new IllegalStateException("Channel already closed!");
close();
}
}
@ -106,10 +106,13 @@ public class Channel implements AutoCloseable {
}
@Override
public synchronized void close() throws Exception {
public synchronized void close() {
if (valid.compareAndSet(true, false)) {
input.close();
output.close();
try {
input.close();
output.close();
} catch (Exception ignore) {
}
}
}

View File

@ -491,7 +491,7 @@ public class ClientConnection extends Thread {
player = new Player(this, username, uuid, Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn(), new PlayerInteractManager());
player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40));
Limbo.getInstance().addPlayer(player);
Limbo.getInstance().getUnsafe().addPlayer(player);
break;
} else if (packetIn instanceof PacketLoginInPluginMessaging) {
PacketLoginInPluginMessaging response = (PacketLoginInPluginMessaging) packetIn;
@ -519,7 +519,7 @@ public class ClientConnection extends Thread {
player = new Player(this, data.getUsername(), data.getUuid(), Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn(), new PlayerInteractManager());
player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40));
Limbo.getInstance().addPlayer(player);
Limbo.getInstance().getUnsafe().addPlayer(player);
break;
}
@ -757,7 +757,7 @@ public class ClientConnection extends Thread {
state = ClientState.DISCONNECTED;
if (player != null) {
Limbo.getInstance().removePlayer(player);
Limbo.getInstance().getUnsafe().removePlayer(player);
}
Limbo.getInstance().getServerConnection().getClients().remove(this);
running = false;

View File

@ -0,0 +1,53 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class ClientboundSetActionBarTextPacket extends PacketOut {
private Component actionBar;
public ClientboundSetActionBarTextPacket(Component actionBar) {
this.actionBar = actionBar;
}
public Component getActionBar() {
return actionBar;
}
@Override
public byte[] serializePacket() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(actionBar), StandardCharsets.UTF_8);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,117 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.bossbar.KeyedBossBar;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PacketPlayOutBoss extends PacketOut {
public enum BossBarAction {
ADD,
REMOVE,
UPDATE_PROGRESS,
UPDATE_NAME,
UPDATE_STYLE,
UPDATE_PROPERTIES;
}
private static int encodeProperties(boolean darkenScreen, boolean playMusic, boolean createWorldFog) {
int i = 0;
if (darkenScreen) {
i |= 1;
}
if (playMusic) {
i |= 2;
}
if (createWorldFog) {
i |= 4;
}
return i;
}
private KeyedBossBar bossBar;
private BossBarAction action;
public PacketPlayOutBoss(KeyedBossBar bossBar, BossBarAction action) {
this.bossBar = bossBar;
this.action = action;
}
public KeyedBossBar getBossBar() {
return bossBar;
}
public BossBarAction getAction() {
return action;
}
@Override
public byte[] serializePacket() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeUUID(output, bossBar.getUniqueId());
DataTypeIO.writeVarInt(output, action.ordinal());
BossBar properties = bossBar.getProperties();
switch (action) {
case ADD: {
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(properties.name()), StandardCharsets.UTF_8);
output.writeFloat(properties.progress());
DataTypeIO.writeVarInt(output, properties.color().ordinal());
DataTypeIO.writeVarInt(output, properties.overlay().ordinal());
output.writeByte(encodeProperties(properties.hasFlag(BossBar.Flag.DARKEN_SCREEN), properties.hasFlag(BossBar.Flag.PLAY_BOSS_MUSIC), properties.hasFlag(BossBar.Flag.CREATE_WORLD_FOG)));
break;
}
case REMOVE: {
break;
}
case UPDATE_PROGRESS: {
output.writeFloat(properties.progress());
break;
}
case UPDATE_NAME: {
DataTypeIO.writeString(output, GsonComponentSerializer.gson().serialize(properties.name()), StandardCharsets.UTF_8);
break;
}
case UPDATE_STYLE: {
DataTypeIO.writeVarInt(output, properties.color().ordinal());
DataTypeIO.writeVarInt(output, properties.overlay().ordinal());
break;
}
case UPDATE_PROPERTIES: {
output.writeByte(encodeProperties(properties.hasFlag(BossBar.Flag.DARKEN_SCREEN), properties.hasFlag(BossBar.Flag.PLAY_BOSS_MUSIC), properties.hasFlag(BossBar.Flag.CREATE_WORLD_FOG)));
break;
}
}
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,112 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.sounds.SoundEffect;
import com.loohp.limbo.utils.DataTypeIO;
import net.kyori.adventure.sound.Sound;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
public class PacketPlayOutNamedSoundEffect extends PacketOut {
private SoundEffect sound;
private Sound.Source source;
private int x;
private int y;
private int z;
private float volume;
private float pitch;
private long seed;
public PacketPlayOutNamedSoundEffect(SoundEffect sound, Sound.Source source, double x, double y, double z, float volume, float pitch, long seed) {
this.sound = sound;
this.source = source;
this.x = (int) (x * 8.0);
this.y = (int) (y * 8.0);
this.z = (int) (z * 8.0);
this.volume = volume;
this.pitch = pitch;
this.seed = seed;
}
public SoundEffect getSound() {
return sound;
}
public Sound.Source getSource() {
return source;
}
public double getX() {
return (float) this.x / 8.0F;
}
public double getY() {
return (float) this.y / 8.0F;
}
public double getZ() {
return (float) this.z / 8.0F;
}
public float getVolume() {
return volume;
}
public float getPitch() {
return pitch;
}
public long getSeed() {
return seed;
}
@Override
public byte[] serializePacket() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
DataTypeIO.writeVarInt(output, 0);
DataTypeIO.writeString(output, sound.getSound().toString(), StandardCharsets.UTF_8);
Optional<Float> fixedRange = sound.fixedRange();
if (fixedRange.isPresent()) {
output.writeBoolean(true);
output.writeFloat(fixedRange.get());
} else {
output.writeBoolean(false);
}
DataTypeIO.writeVarInt(output, source.ordinal());
output.writeInt(x);
output.writeInt(y);
output.writeInt(z);
output.writeFloat(volume);
output.writeFloat(pitch);
output.writeLong(seed);
return buffer.toByteArray();
}
}

View File

@ -0,0 +1,74 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.network.protocol.packets;
import com.loohp.limbo.utils.DataTypeIO;
import com.loohp.limbo.utils.NamespacedKey;
import net.kyori.adventure.sound.Sound;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class PacketPlayOutStopSound extends PacketOut {
private NamespacedKey sound;
private Sound.Source source;
public PacketPlayOutStopSound(NamespacedKey sound, Sound.Source source) {
this.sound = sound;
this.source = source;
}
public NamespacedKey getSound() {
return sound;
}
public Sound.Source getSource() {
return source;
}
@Override
public byte[] serializePacket() throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(buffer);
output.writeByte(Packet.getPlayOut().get(getClass()));
if (source != null) {
if (sound != null) {
output.writeByte(3);
DataTypeIO.writeVarInt(output, source.ordinal());
DataTypeIO.writeString(output, sound.toString(), StandardCharsets.UTF_8);
} else {
output.writeByte(1);
DataTypeIO.writeVarInt(output, source.ordinal());
}
} else if (sound != null) {
output.writeByte(2);
DataTypeIO.writeString(output, sound.toString(), StandardCharsets.UTF_8);
} else {
output.writeByte(0);
}
return buffer.toByteArray();
}
}

View File

@ -31,6 +31,7 @@ import com.loohp.limbo.events.player.PlayerTeleportEvent;
import com.loohp.limbo.location.Location;
import com.loohp.limbo.network.ClientConnection;
import com.loohp.limbo.network.protocol.packets.ClientboundClearTitlesPacket;
import com.loohp.limbo.network.protocol.packets.ClientboundSetActionBarTextPacket;
import com.loohp.limbo.network.protocol.packets.ClientboundSetSubtitleTextPacket;
import com.loohp.limbo.network.protocol.packets.ClientboundSetTitleTextPacket;
import com.loohp.limbo.network.protocol.packets.ClientboundSetTitlesAnimationPacket;
@ -38,10 +39,13 @@ import com.loohp.limbo.network.protocol.packets.ClientboundSystemChatPacket;
import com.loohp.limbo.network.protocol.packets.PacketOut;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutGameState;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutHeldItemChange;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutNamedSoundEffect;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutPlayerListHeaderFooter;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutPositionAndLook;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutResourcePackSend;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutRespawn;
import com.loohp.limbo.network.protocol.packets.PacketPlayOutStopSound;
import com.loohp.limbo.sounds.SoundEffect;
import com.loohp.limbo.utils.BungeecordAdventureConversionUtils;
import com.loohp.limbo.utils.GameMode;
import com.loohp.limbo.utils.MessageSignature;
@ -50,6 +54,7 @@ import net.kyori.adventure.audience.MessageType;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.inventory.Book;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.sound.Sound.Emitter;
import net.kyori.adventure.sound.SoundStop;
@ -65,6 +70,7 @@ import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
public class Player extends LivingEntity implements CommandSender {
@ -448,7 +454,13 @@ public class Player extends LivingEntity implements CommandSender {
@Override
public void stopSound(SoundStop stop) {
throw new UnsupportedOperationException("This function has not been implemented yet.");
Key sound = stop.sound();
PacketPlayOutStopSound stopSound = new PacketPlayOutStopSound(sound == null ? null : NamespacedKey.fromKey(sound), stop.source());
try {
clientConnection.sendPacket(stopSound);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
@ -458,27 +470,37 @@ public class Player extends LivingEntity implements CommandSender {
@Override
public void playSound(Sound sound, double x, double y, double z) {
throw new UnsupportedOperationException("This function has not been implemented yet.");
PacketPlayOutNamedSoundEffect namedSoundEffect = new PacketPlayOutNamedSoundEffect(
SoundEffect.createVariableRangeEvent(NamespacedKey.fromKey(sound.name())),
sound.source(), x, y, z, sound.volume(), sound.pitch(), sound.seed().orElse(ThreadLocalRandom.current().nextLong())
);
try {
clientConnection.sendPacket(namedSoundEffect);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void playSound(Sound sound) {
throw new UnsupportedOperationException("This function has not been implemented yet.");
playSound(sound, x, y, z);
}
@Override
public void sendActionBar(Component message) {
try {
ClientboundSystemChatPacket chat = new ClientboundSystemChatPacket(message, true);
clientConnection.sendPacket(chat);
} catch (IOException ignored) {}
ClientboundSetActionBarTextPacket setActionBar = new ClientboundSetActionBarTextPacket(message);
clientConnection.sendPacket(setActionBar);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void sendPlayerListHeaderAndFooter(Component header, Component footer) {
try {
PacketPlayOutPlayerListHeaderFooter packsend = new PacketPlayOutPlayerListHeaderFooter(header, footer);
clientConnection.sendPacket(packsend);
PacketPlayOutPlayerListHeaderFooter listHeaderFooter = new PacketPlayOutPlayerListHeaderFooter(header, footer);
clientConnection.sendPacket(listHeaderFooter);
} catch (IOException e) {
e.printStackTrace();
}

View File

@ -40,88 +40,82 @@ public class Tick {
private Queue<LimboSchedulerTask> asyncTasksQueue = new ConcurrentLinkedQueue<>();
public Tick(Limbo instance) {
new Thread(new Runnable() {
@Override
public void run() {
tickingInterval = (int) Math.round(1000.0 / Limbo.getInstance().getServerProperties().getDefinedTicksPerSecond());
for (int i = 0; i < 4; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (instance.isRunning()) {
LimboSchedulerTask task = asyncTasksQueue.poll();
if (task == null) {
try {
TimeUnit.NANOSECONDS.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
LimboTask limboTask = task.getTask();
try {
limboTask.run();
} catch (Throwable e) {
System.err.println("Task " + task.getTaskId() + " threw an exception: " + e.getLocalizedMessage());
e.printStackTrace();
}
}
}
}
});
thread.start();
threads.add(thread);
}
while (instance.isRunning()) {
long start = System.currentTimeMillis();
tick.incrementAndGet();
instance.getPlayers().forEach(each -> {
if (each.clientConnection.isReady()) {
new Thread(() -> {
tickingInterval = (int) Math.round(1000.0 / Limbo.getInstance().getServerProperties().getDefinedTicksPerSecond());
for (int i = 0; i < 4; i++) {
Thread thread = new Thread(() -> {
while (instance.isRunning()) {
LimboSchedulerTask task = asyncTasksQueue.poll();
if (task == null) {
try {
each.playerInteractManager.update();
} catch (IOException e) {
TimeUnit.NANOSECONDS.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
try {
each.getDataWatcher().update();
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
*/
}
});
instance.getWorlds().forEach(each -> {
try {
each.update();
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
});
CurrentSchedulerTask tasks = instance.getScheduler().collectTasks(getCurrentTick());
if (tasks != null) {
asyncTasksQueue.addAll(tasks.getAsyncTasks());
tasks.getSyncedTasks().forEach(task -> {
LimboTask limboTask = task.getTask();
} else {
LimboTask limboTask = task.getTask();
try {
limboTask.run();
} catch (Throwable e) {
System.err.println("Task " + task.getTaskId() + " threw an exception: " + e.getLocalizedMessage());
e.printStackTrace();
}
});
}
long end = System.currentTimeMillis();
try {
TimeUnit.MILLISECONDS.sleep(tickingInterval - (end - start));
} catch (InterruptedException e) {
}
}
});
thread.start();
threads.add(thread);
}
while (instance.isRunning()) {
long start = System.currentTimeMillis();
tick.incrementAndGet();
instance.getPlayers().forEach(each -> {
if (each.clientConnection.isReady()) {
try {
each.playerInteractManager.update();
} catch (IOException e) {
e.printStackTrace();
}
/*
try {
each.getDataWatcher().update();
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
*/
}
});
instance.getWorlds().forEach(each -> {
try {
each.update();
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
});
CurrentSchedulerTask tasks = instance.getScheduler().collectTasks(getCurrentTick());
if (tasks != null) {
asyncTasksQueue.addAll(tasks.getAsyncTasks());
tasks.getSyncedTasks().forEach(task -> {
LimboTask limboTask = task.getTask();
try {
limboTask.run();
} catch (Throwable e) {
System.err.println("Task " + task.getTaskId() + " threw an exception: " + e.getLocalizedMessage());
e.printStackTrace();
}
});
}
long end = System.currentTimeMillis();
try {
TimeUnit.MILLISECONDS.sleep(tickingInterval - (end - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}

View File

@ -0,0 +1,61 @@
/*
* This file is part of Limbo.
*
* Copyright (C) 2022. LoohpJames <jamesloohp@gmail.com>
* Copyright (C) 2022. Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.loohp.limbo.sounds;
import com.loohp.limbo.utils.NamespacedKey;
import java.util.Optional;
public class SoundEffect {
public static SoundEffect createVariableRangeEvent(NamespacedKey namespacedKey) {
return new SoundEffect(namespacedKey, 16.0F, false);
}
public static SoundEffect createFixedRangeEvent(NamespacedKey namespacedKey, float range) {
return new SoundEffect(namespacedKey, range, true);
}
private final NamespacedKey sound;
private final float range;
private final boolean newSystem;
private SoundEffect(NamespacedKey sound, float range, boolean newSystem) {
this.sound = sound;
this.range = range;
this.newSystem = newSystem;
}
public NamespacedKey getSound() {
return sound;
}
public float getRange() {
return range;
}
public boolean isNewSystem() {
return newSystem;
}
public Optional<Float> fixedRange() {
return this.newSystem ? Optional.of(this.range) : Optional.empty();
}
}

View File

@ -19,12 +19,25 @@
package com.loohp.limbo.utils;
import com.loohp.limbo.plugins.LimboPlugin;
import net.kyori.adventure.key.Key;
import java.util.Objects;
public class NamespacedKey {
public static final String MINECRAFT_KEY = "minecraft";
private String namespace;
private String key;
public static NamespacedKey minecraft(String key) {
return new NamespacedKey(MINECRAFT_KEY, key);
}
public static NamespacedKey fromKey(Key key) {
return new NamespacedKey(key.namespace(), key.value());
}
private final String namespace;
private final String key;
public NamespacedKey(String namespacedKey) {
int index = namespacedKey.indexOf(":");
@ -37,14 +50,14 @@ public class NamespacedKey {
}
}
public NamespacedKey(LimboPlugin plugin, String key) {
this(plugin.getName().toLowerCase().replace(" ", "_"), key);
}
public NamespacedKey(String namespace, String key) {
this.namespace = namespace;
this.key = key;
}
public static NamespacedKey minecraft(String key) {
return new NamespacedKey(MINECRAFT_KEY, key);
}
public String getNamespace() {
return namespace;
@ -54,40 +67,25 @@ public class NamespacedKey {
return key;
}
public Key toKey() {
return Key.key(namespace, key);
}
@Override
public String toString() {
return namespace + ":" + key;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
result = prime * result + ((namespace == null) ? 0 : namespace.hashCode());
return result;
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
NamespacedKey that = (NamespacedKey) o;
return namespace.equals(that.namespace) && key.equals(that.key);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NamespacedKey other = (NamespacedKey) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
if (namespace == null) {
if (other.namespace != null)
return false;
} else if (!namespace.equals(other.namespace))
return false;
return true;
public int hashCode() {
return Objects.hash(namespace, key);
}
}

View File

@ -49,7 +49,11 @@
"ClientboundSetTitlesAnimationPacket": "0x5C",
"ClientboundSetTitleTextPacket": "0x5B",
"ClientboundSetSubtitleTextPacket": "0x59",
"ClientboundClearTitlesPacket": "0x0C"
"ClientboundSetActionBarTextPacket": "0x42",
"ClientboundClearTitlesPacket": "0x0C",
"PacketPlayOutBoss": "0x0A",
"PacketPlayOutNamedSoundEffect": "0x5E",
"PacketPlayOutStopSound": "0x5F"
},
"StatusIn": {
"0x01": "PacketStatusInPing",