All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.WorldHost Maven / Gradle / Ivy

/** Copyright 2014 Robin Stumm ([email protected], http://dermetfan.net)
 *
 *  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 net.dermetfan.gdx.physics.box2d.kryonet.multiplayer;

import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.Joint;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Pools;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.minlog.Log;
import net.dermetfan.gdx.physics.box2d.WorldObserver;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.UpdateRequest;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.UpdateRequest.Refusal;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.Change;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.Creation;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.Destruction;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.Update;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.body.BodyChange;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.body.BodyCreation;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.fixture.FixtureChange;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.fixture.FixtureCreation;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.joint.JointChange;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.joint.JointCreation;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.world.WorldChange;
import net.dermetfan.gdx.physics.box2d.Box2DUtils;
import net.dermetfan.gdx.utils.DualIntObjectMap;
import net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.Packet;

import static com.esotericsoftware.minlog.Log.TRACE;

public class WorldHost extends Listener implements WorldObserver.Listener {

	/** @param kryo the Kryo to register the classes needed with */
	public static void registerClasses(Kryo kryo) {
		// packets
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.UpdateRequest.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.UpdateRequest.Refusal.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.UpdateProposal.class);

		// updates
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.Update.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.Change.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.Creation.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.Destruction.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.world.WorldChange.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.body.BodyChange.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.body.BodyCreation.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.fixture.FixtureChange.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.fixture.FixtureCreation.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.joint.JointChange.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.kryonet.multiplayer.packets.updates.joint.JointCreation.class);

		// WorldObserver
		kryo.register(net.dermetfan.gdx.physics.box2d.WorldObserver.Change.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.WorldObserver.WorldChange.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.WorldObserver.BodyChange.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.WorldObserver.FixtureChange.class);
		kryo.register(net.dermetfan.gdx.physics.box2d.WorldObserver.JointChange.class);

		// Box2D
		kryo.register(com.badlogic.gdx.physics.box2d.Transform.class);
		kryo.register(com.badlogic.gdx.physics.box2d.MassData.class);
		kryo.register(com.badlogic.gdx.physics.box2d.BodyDef.class);
		kryo.register(com.badlogic.gdx.physics.box2d.BodyDef.BodyType.class);
		kryo.register(com.badlogic.gdx.physics.box2d.Filter.class);
		kryo.register(com.badlogic.gdx.physics.box2d.Shape.Type.class);
		kryo.register(com.badlogic.gdx.physics.box2d.JointDef.class);

		// libGDX
		kryo.register(com.badlogic.gdx.math.Vector2.class);

		// java
		kryo.register(float[].class);
		kryo.register(Class.class);
	}

	private final Array clients = new Array<>(30);

	private final DualIntObjectMap index = new DualIntObjectMap<>();

	private WorldObserver observer;

	@Override
	public void received(Connection connection, Object obj) {
		if(obj instanceof UpdateRequest) {
			UpdateRequest request = (UpdateRequest) obj;
			Object object = index.getValue(request.getObjectHash());
			if(object == null) {
				if(TRACE)
					Log.trace("WorldHost", "received UpdateRequest with unknown object hash " + request.getObjectHash() + ", refusing");
				Refusal refusal = Pools.obtain(Refusal.class);
				refusal.setRequest(request);
				send(refusal, connection);
				Pools.free(refusal);
				return;
			}
			if(TRACE)
				Log.trace("WorldHost", "received UpdateRequest of type " + ClassReflection.getSimpleName(request.getType()) + " for " + object + " with hash " + request.getObjectHash());
			Update update = Pools.obtain(request.getType());
			if(update instanceof Creation) {
				if(object instanceof Body) {
					BodyCreation creation = Pools.obtain(BodyCreation.class);
					creation.setBodyDef(Box2DUtils.createDef((Body) object));
					update = creation;
				} else if(object instanceof Fixture) {
					FixtureCreation creation = Pools.obtain(FixtureCreation.class);
					Fixture fixture = (Fixture) object;
					creation.setFixtureDef(Box2DUtils.createDef(fixture));
					creation.setBodyHash(getObjectHash(fixture.getBody()));
					update = creation;
				} else if(object instanceof Joint) {
					JointCreation creation = Pools.obtain(JointCreation.class);
					creation.setJointDef(null /* TODO */);
					update = creation;
				}
			} else if(update instanceof Change) {
				if(update instanceof WorldChange)
					((WorldChange) update).setChange(observer.getWorldChange());
				if(update instanceof BodyChange)
					((BodyChange) update).setChange(observer.getBodyChange(update.getObjectHash()));
				else if(update instanceof FixtureChange)
					((FixtureChange) update).setChange(observer.getFixtureChange(update.getObjectHash()));
				else if(update instanceof JointChange)
					((JointChange) update).setChange(observer.getJointChange((Joint) object));
			} else if(update instanceof Destruction)
				update = Pools.obtain(Destruction.class);
			update.setObjectHash(request.getObjectHash());
			send(update, connection);
			Pools.free(update);
		}
	}

	@Override
	public void setOn(WorldObserver observer) {
		if(this.observer != null)
			throw new UnsupportedOperationException("a WorldHost may only be set on a single WorldObserver");
		this.observer = observer;
	}

	@Override
	public void removedFrom(WorldObserver observer) {
		this.observer = null;
	}

	@Override
	public void changed(World world, WorldObserver.WorldChange change) {
		WorldChange packet = Pools.obtain(WorldChange.class);
		send(packet);
		Pools.free(packet);
	}

	@Override
	public void changed(Body body, WorldObserver.BodyChange change) {
		BodyChange packet = Pools.obtain(BodyChange.class);
		packet.setObjectHash(getObjectHash(body));
		packet.setChange(change);
		send(packet);
		Pools.free(packet);
	}

	@Override
	public void created(Body body) {
		int objectHash = com.badlogic.gdx.physics.box2d.Box2DUtils.hashCode(body);
		index.put(objectHash, body);
		if(TRACE)
			Log.trace("WorldHost", "new Body created, registering with hash " + objectHash);
		BodyCreation packet = Pools.obtain(BodyCreation.class);
		packet.setObjectHash(objectHash);
		packet.setBodyDef(Box2DUtils.createDef(body));
		send(packet);
		Pools.free(packet);
	}

	@Override
	public void destroyed(Body body) {
		int objectHash = getObjectHash(body);
		index.removeByKey(objectHash);
		if(TRACE)
			Log.trace("WorldHost", "Body with hash " + objectHash + " destroyed");
		Destruction packet = Pools.obtain(Destruction.class);
		packet.setObjectHash(objectHash);
		send(packet);
		Pools.free(packet);
	}

	@Override
	public void changed(Fixture fixture, WorldObserver.FixtureChange change) {
		FixtureChange packet = Pools.obtain(FixtureChange.class);
		packet.setObjectHash(getObjectHash(fixture));
		packet.setChange(change);
		send(packet);
		Pools.free(packet);
	}

	@Override
	public void created(Fixture fixture) {
		int objectHash = com.badlogic.gdx.physics.box2d.Box2DUtils.hashCode(fixture);
		index.put(objectHash, fixture);
		if(TRACE)
			Log.trace("WorldHost", "new Fixture created, registering with hash " + objectHash);
		FixtureCreation packet = Pools.obtain(FixtureCreation.class);
		packet.setObjectHash(objectHash);
		packet.setBodyHash(getObjectHash(fixture.getBody()));
		packet.setFixtureDef(Box2DUtils.createDef(fixture));
		send(packet);
		Pools.free(packet);
	}

	@Override
	public void destroyed(Fixture fixture) {
		int objectHash = getObjectHash(fixture);
		index.removeByKey(objectHash);
		if(TRACE)
			Log.trace("WorldHost", "Fixture with hash " + objectHash + " destroyed");
		Destruction packet = Pools.obtain(Destruction.class);
		packet.setObjectHash(objectHash);
		send(packet);
		Pools.free(packet);
	}

	@Override
	public void changed(Joint joint, WorldObserver.JointChange change) {
		JointChange packet = Pools.obtain(JointChange.class);
		packet.setObjectHash(getObjectHash(joint));
		packet.setChange(change);
		send(packet);
		Pools.free(packet);
	}

	@Override
	public void created(Joint joint) {
		int objectHash = com.badlogic.gdx.physics.box2d.Box2DUtils.hashCode(joint);
		index.put(objectHash, joint);
		if(TRACE)
			Log.trace("WorldHost", "new Joint created, registering with hash " + objectHash);
		JointCreation packet = Pools.obtain(JointCreation.class);
		packet.setObjectHash(objectHash);
		packet.setJointDef(null /* TODO */);
		send(packet);
		Pools.free(packet);
	}

	@Override
	public void destroyed(Joint joint) {
		int objectHash = index.removeByValue(joint, 0);
		if(TRACE)
			Log.trace("WorldHost", "Joint with hash " + objectHash + " destroyed");
		Destruction packet = Pools.obtain(Destruction.class);
		packet.setObjectHash(objectHash);
		send(packet);
		Pools.free(packet);
	}

	// convenience methods

	private int getObjectHash(Object object) {
		return index.getKey(object, 0);
	}

	private Object getObject(int hash) {
		Object object = index.getValue(hash);
		assert object != null;
		return object;
	}

	private void send(Packet packet) {
		for(Connection client : clients)
			send(packet, client);
	}

	private void send(Packet packet, Connection connection) {
		if(packet.isOptional())
			connection.sendUDP(packet);
		else
			connection.sendTCP(packet);
	}

	// getters and setters

	public Array getClients() {
		return clients;
	}

}