-
Notifications
You must be signed in to change notification settings - Fork 1
Creating your own server and more
Want a more detailed example? Check out my server in my game
Servers have worlds just like the game.
It starts with the interface World
which has methods for many common methods that you would use. ServerWorld
implements World
which just handles collections of entities and players. Then, AbstractServerWorld
implements ServerWorld
.
AbstractServerWorld
requires two types, an instance of a server player and an instance of a server entity. It could either be the default implementation LunarServerEntityPlayer
, LunarServerEntity
or your own types that extend those classes.
Here is a small example of a custom world
public class CrimsonServerWorld extends AbstractServerWorld<CrimsonPlayer, CrimsonEntity> {
public CrimsonServerWorld(ServerWorldConfiguration configuration, String worldName) {
super(configuration, worldName);
}
}
With the game server we can then add this world
worldManager = new CrimsonWorldManager();
gameServer.setWorldManager(worldManager);
gameServer.getWorldManager().addWorld("TutorialWorld", new CrimsonServerWorld(new ServerWorldConfiguration(), "TutorialWorld"));
Extending ServerPlayerConnection
allows us to implement alot of game logic that we need, here is an example from my game
public final class CrimsonPlayerConnection extends ServerPlayerConnection {
private CrimsonWorld worldIn;
private CrimsonPlayer localPlayer;
public CrimsonPlayerConnection(Channel channel, LunarServer server) {
super(channel, server);
registerPacket(ClientSpawnEntity.ID, ClientSpawnEntity::new, this::handleSpawnEntity);
registerPacket(ClientEquipItem.ID, ClientEquipItem::new, this::handleEquipItem);
registerPacket(ClientSwingItem.ID, ClientSwingItem::new, this::handleSwingItem);
}
@Override
public void handleAuthentication(CPacketAuthentication packet) {
Logging.info(this, "Attempting to authenticate a new player from [" + channel.localAddress() + "]");
super.handleAuthentication(packet);
}
@Override
public void handleJoinWorld(CPacketJoinWorld packet) {
Logging.info(this, "New player requesting to join world: " + packet.getWorldName() + " with username " + packet.getUsername());
if (packet.getUsername() == null || packet.getUsername().isEmpty()) {
this.sendImmediately(new SPacketWorldInvalid(packet.getWorldName(), "Invalid username."));
return;
} else if ((packet.getWorldName() == null || packet.getWorldName().isEmpty()) || !server.getWorldManager().worldExists(packet.getWorldName())) {
this.sendImmediately(new SPacketWorldInvalid(packet.getWorldName(), "World does not exist."));
return;
}
final World world = server.getWorldManager().getWorld(packet.getWorldName());
if (world.isFull()) {
this.sendImmediately(new SPacketWorldInvalid(packet.getWorldName(), "World is full."));
return;
}
this.worldIn = (CrimsonWorld) world;
this.localPlayer = new CrimsonPlayer(true, server, this);
this.localPlayer.setEntityName(packet.getUsername());
this.localPlayer.setWorldIn(world);
this.localPlayer.setEntityId(world.assignEntityIdFor(true));
this.player = localPlayer;
sendImmediately(new SPacketJoinWorld(packet.getWorldName(), localPlayer.getEntityId()));
}
@Override
public void handleWorldLoaded(CPacketWorldLoaded packet) {
super.handleWorldLoaded(packet);
worldIn.handlePlayerLoaded(localPlayer);
}
private void handleSpawnEntity(ClientSpawnEntity packet) {
Logging.info(this, "Spawning a new entity by request from player, type=" + packet.getType() + ", pos=" + packet.getPosition());
if (worldIn != null) {
worldIn.spawnEntityInWorld(packet.getType(), packet.getPosition());
final ServerSpawnEntity entity = new ServerSpawnEntity(worldIn.assignEntityIdFor(false), packet.getType(), packet.getPosition());
sendImmediately(entity);
}
}
private void handleEquipItem(ClientEquipItem packet) {
worldIn.broadcastNowWithExclusion(packet.getEntityId(), new ServerPlayerEquippedItem(packet.getEntityId(), packet.getItemId()));
}
private void handleSwingItem(ClientSwingItem packet) {
worldIn.broadcastNowWithExclusion(packet.getEntityId(), new ServerPlayerSwungItem(packet.getEntityId(), packet.getItemId()));
}
}
To have each new connection use that class, simply:
server = new NettyServer(ip, port, protocol, gameServer);
server.setConnectionProvider(socketChannel -> new CrimsonPlayerConnection(socketChannel, gameServer));
server.bind();
Getting Started
Server
Networking
Entities
Worlds
Instances