Skip to content

Commit

Permalink
Chunk data streaming for newly connected player
Browse files Browse the repository at this point in the history
  • Loading branch information
bloodrizer committed Jun 27, 2012
1 parent 1c7e47e commit e771e7e
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 25 deletions.
@@ -1,7 +1,6 @@
package com.nuclearunicorn.negame.server;

import com.nuclearunicorn.negame.client.NEGame;
import com.nuclearunicorn.negame.client.clientIo.NettyClient;
import com.nuclearunicorn.negame.client.game.modes.in_game.InGameMode;
import com.nuclearunicorn.negame.client.game.modes.main_menu.MainMenuMode;
import com.nuclearunicorn.negame.server.core.NEServerCore;
Expand All @@ -17,10 +16,11 @@ public class Main {

public static NEGame game;
public static InGameMode inGameMode = new InGameMode();
public static NEServerCore serverCore;

public static void main(String[] args) {
/* Run NE Server */
NEServerCore serverCore = new NEServerCore();
serverCore = new NEServerCore();
serverCore.run();

/* Run Client in the same session and connect it to the server */
Expand All @@ -39,7 +39,7 @@ public static void main(String[] args) {
finally{
//when client is done, terminate server
game.running = false;
NettyClient.destroy();
//NettyClient.destroy();
serverCore.destroy();
}
}
Expand Down
Expand Up @@ -38,6 +38,7 @@
*/
public class CharServer extends AServerIoLayer {
NioServerSocketChannelFactory nio_factory;

ArrayList<PlayerData> playerData = new ArrayList<PlayerData>();
ChannelPipelineFactory factory;

Expand Down Expand Up @@ -129,4 +130,8 @@ protected void handlePacket(NEDataPacket packet) {
}
}

public ArrayList<PlayerData> getPlayerData() {
return playerData;
}

}
Expand Up @@ -5,12 +5,14 @@

package com.nuclearunicorn.negame.server.core;

import java.util.ArrayList;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.ChannelGroupFuture;
import org.jboss.netty.channel.group.DefaultChannelGroup;

import java.util.ArrayList;

/**
*
* @author Administrator
Expand Down Expand Up @@ -51,10 +53,19 @@ public void update(){

for (NEDataPacket packet: packets){
//System.out.println(name+":handling packet #");
handlePacket(packet);
try{
handlePacket(packet);
}catch(Exception ex){
ex.printStackTrace();
}
}
packets.clear();
}

protected static void sendMsg(String msg, Channel ioChannel){
System.err.println("IO layer>: sending message '"+msg+"");
ioChannel.write(msg+"\r\n");
}

protected abstract void handlePacket(NEDataPacket packet);
}
Expand Up @@ -14,6 +14,7 @@
* @author bloodrizer
*/
public class NEServerCore implements IServer {

CharServer charServer;
GameServer gameServer;

Expand Down Expand Up @@ -41,4 +42,8 @@ public void update() {
public GameEnvironment getEnv() {
return gameServer.getEnv();
}

public CharServer getCharServer() {
return charServer;
}
}
Expand Up @@ -4,9 +4,11 @@
*/
package com.nuclearunicorn.negame.server.core;

import org.jboss.netty.channel.Channel;

import java.net.InetSocketAddress;
import java.util.HashMap;
import org.jboss.netty.channel.Channel;
import java.util.Set;

/**
*
Expand Down Expand Up @@ -60,4 +62,27 @@ private static User attachChannel(Channel channel){
throw new RuntimeException("Trying to attach channel to unregistered user ip");
}
}

public static Set<Channel> getActiveChannels() {
return channel2user.keySet();
}

/*
Returns io channel session assigned to current user, null otherwise
*/
public static Channel getUserChannel(User observer) {
for (Channel channel : channel2user.keySet()){
if(channel2user.get(channel).equals(observer)){
return channel;
}
}
return null;
}

/*
Check if given channel session is assigned to user
*/
public static boolean isUserChannel(User observer, Channel channel) {
return channel2user.get(channel).equals(observer);
}
}
@@ -0,0 +1,22 @@
package com.nuclearunicorn.negame.server.game.world.entities;

import com.nuclearunicorn.libroguelike.game.ent.EntityNPC;
import com.nuclearunicorn.negame.server.core.User;

/**
* Created with IntelliJ IDEA.
* User: Administrator
* Date: 28.06.12
* Time: 0:21
* To change this template use File | Settings | File Templates.
*/
public class EntityPlayerNPC extends EntityNPC {
User user;
public EntityPlayerNPC(User user){
this.user = user;
}

public User getUser() {
return user;
}
}
Expand Up @@ -5,10 +5,14 @@

package com.nuclearunicorn.negame.server.gameserver;

import com.nuclearunicorn.libroguelike.events.EEntitySpawn;
import com.nuclearunicorn.libroguelike.events.Event;
import com.nuclearunicorn.libroguelike.events.EventManager;
import com.nuclearunicorn.libroguelike.events.IEventListener;
import com.nuclearunicorn.libroguelike.events.network.EEntitySpawnNetwork;
import com.nuclearunicorn.libroguelike.events.network.NetworkEvent;
import com.nuclearunicorn.libroguelike.game.GameEnvironment;
import com.nuclearunicorn.libroguelike.game.ent.Entity;
import com.nuclearunicorn.libroguelike.game.ent.EntityNPC;
import com.nuclearunicorn.libroguelike.game.ent.controller.NpcController;
import com.nuclearunicorn.libroguelike.game.world.WorldChunk;
import com.nuclearunicorn.libroguelike.game.world.WorldModel;
Expand All @@ -20,6 +24,7 @@
import com.nuclearunicorn.negame.server.core.ServerUserPool;
import com.nuclearunicorn.negame.server.core.User;
import com.nuclearunicorn.negame.server.game.world.ServerWorldModel;
import com.nuclearunicorn.negame.server.game.world.entities.EntityPlayerNPC;
import com.nuclearunicorn.negame.server.generators.NEServerGroundChunkGenerator;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
Expand All @@ -35,14 +40,16 @@

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;


/**
*
* @author Administrator
*/
public class GameServer extends AServerIoLayer {
public class GameServer extends AServerIoLayer implements IEventListener {
NioServerSocketChannelFactory nio_factory;


Expand Down Expand Up @@ -79,6 +86,7 @@ public WorldModel getWorld() {
ArrayList<WorldLayer> layers = new ArrayList<WorldLayer>(model.getLayers());
layers.get(0).registerGenerator(new NEServerGroundChunkGenerator());

gameEnv.getEventManager().subscribe(this);
}


Expand Down Expand Up @@ -202,7 +210,7 @@ public void spawnPlayerCharacter(User user) {

GameEnvironment env = getEnv();

Entity mplayer_ent = new EntityNPC();
Entity mplayer_ent = new EntityPlayerNPC(user);
mplayer_ent.setEnvironment(env);
mplayer_ent.set_controller(new NpcController(env));

Expand All @@ -215,7 +223,19 @@ public void spawnPlayerCharacter(User user) {
mplayer_ent.spawn(getUserLocation(user));

user.setEntity(mplayer_ent);
worldUpdateLazyLoad(0,0);
//worldUpdateLazyLoad(0,0);

//stream 3x3 chunk data
WorldChunk chunk = mplayer_ent.get_chunk();
//notifyChunkData(user);

WorldLayer serverGroundLayer = getEnv().getWorldLayer(WorldLayer.GROUND_LAYER);
for (int i = chunk.origin.getX()-1; i<=chunk.origin.getX(); i++)
for (int j = chunk.origin.getY()-1; j<=chunk.origin.getY(); j++){
//if chunk doest not exist, it will be generated and populated with game objects
WorldChunk cachedChunk = serverGroundLayer.get_cached_chunk(i, j);
notifyChunkData(user, cachedChunk);
}
}

/**
Expand All @@ -241,4 +261,85 @@ private void worldUpdateLazyLoad(int x, int y){
}
}
}


@Override
public void e_on_event(Event event) {
if (event instanceof EEntitySpawn){
if (((EEntitySpawn) event).ent instanceof EntityPlayerNPC){
EntityPlayerNPC entPlayerNpc = (EntityPlayerNPC)((EEntitySpawn) event).ent;

//if player is spawned on server, notify every other player of this event
User npcUser = entPlayerNpc.getUser();
broadcostUserEvent(event, npcUser);
}
}
}

/*
This method takes all entities in the chunk and stream them to the player
TODO: introduce some compact bundle format a-la json
Note that this method passes simple data only, i.e. coords, uid and simple ent type
Client responsibility is to request additional information about given entity
*/
public void notifyChunkData(User observer, WorldChunk chunk){
Entity userEnt = observer.getEntity();
Channel userChannel = ServerUserPool.getUserChannel(observer);
//userEnt.origin;
//Point chunkCoord = WorldChunk.get_chunk_coord(userEnt.origin);
//WorldChunk chunk = userEnt.get_chunk();
List<Entity> entityList = chunk.getEntList();

System.err.println("Sending chunk data to user #"+observer.getId() + "(" + entityList.size() + " entities total)");
for (Entity chunkEnt: entityList){
if (!chunkEnt.equals(userEnt)){
EEntitySpawnNetwork spawnEvent = new EEntitySpawnNetwork(userEnt, userEnt.origin);
sendEvent(spawnEvent, userChannel);
}
}
}

/**
Broadcost user-triggered event to every user except observer
Typical situation is player spawn, when everyone should be aware of this event except user itself
*/
private void broadcostUserEvent(Event event, User observer){
if (event.is_local()){
return;
}

Set<Channel> activeChannels = ServerUserPool.getActiveChannels();
for(Channel channel: activeChannels){
if (!ServerUserPool.isUserChannel(observer, channel)){
sendEvent(event, channel);
}
}
}

private void broadcostEvent(Event event){
if (event.is_local()){
return;
}

Set<Channel> activeChannels = ServerUserPool.getActiveChannels();
for(Channel channel: activeChannels){
sendEvent(event, channel);
}
}
/*
Todo: this method looks similar to the client logic, probably need to extract as EventDispatcher class
*/
private void sendEvent(Event event, Channel channel) {
NetworkEvent networkEvent = (NetworkEvent)event;

String[] tokens = networkEvent.serialize();
StringBuilder sb = new StringBuilder();

sb.append(event.classname().concat(" "));

for (int i = 1; i<tokens.length; i++){
sb.append(tokens[i].concat(" "));
}
sendMsg(sb.toString(), channel);
}
}
Expand Up @@ -50,7 +50,11 @@ public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {


//spawn player entity on server side
getServer().spawnPlayerCharacter(user);
try{
getServer().spawnPlayerCharacter(user);
}catch (Exception ex){
ex.printStackTrace();
}

//get entity parameters and push them back to the client since we need to keep channel context
int x = user.getEntity().x();
Expand Down
Expand Up @@ -11,4 +11,5 @@ public class EventConstants {
public static final String E_SELECT_CHARACTER = "com.nuclearunicorn.libroguelike.events.network.ESelectCharacter";
public static final String E_PLAYER_LOGON = "com.nuclearunicorn.libroguelike.events.network.EPlayerSpawn";
public static final String E_ENTITY_SET_PATH = "com.nuclearunicorn.libroguelike.events.network.EEntitySetPath";
public static final String E_ENTITY_SPAWN_NETWORK = "com.nuclearunicorn.libroguelike.events.network.EEntitySpawnNetwork";
}
@@ -1,6 +1,7 @@
package com.nuclearunicorn.negame.client;

import com.nuclearunicorn.libroguelike.core.Game;
import com.nuclearunicorn.negame.client.clientIo.NettyClient;
import com.nuclearunicorn.negame.common.IServer;

/**
Expand All @@ -23,10 +24,18 @@ public void attachServerSession(IServer server){
}

public void updateServerSession(){
server.update();
if (server != null){
server.update();
}
}

public void initStateUI() {
activeMode.get_ui().init();
}

@Override
public void run() {
super.run();
NettyClient.destroy();
}
}

0 comments on commit e771e7e

Please sign in to comment.