package org.jrenner.fps; import com.badlogic.gdx.Application; import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.InputMultiplexer; import com.badlogic.gdx.utils.GdxRuntimeException; import com.badlogic.gdx.utils.TimeUtils; import org.jrenner.fps.effects.BulletHit; import org.jrenner.fps.entity.Entity; import org.jrenner.fps.event.ClientEventManager; import org.jrenner.fps.input.GestureHandler; import org.jrenner.fps.input.Input; import org.jrenner.fps.move.Movement; import org.jrenner.fps.net.AbstractServer; import org.jrenner.fps.net.LocalServer; import org.jrenner.fps.net.NetManager; import org.jrenner.fps.net.NetServer; import org.jrenner.fps.net.ServerType; import org.jrenner.fps.net.client.AbstractClient; import org.jrenner.fps.net.client.LocalClient; import org.jrenner.fps.net.client.NetClient; import org.jrenner.fps.particles.Particles; import org.jrenner.fps.utils.Pooler; public class Main extends ApplicationAdapter { public static Main inst; private View view; public Input input; // TODO move to a field of AbstractClient public ClientEventManager clientEventManager; private Physics physics; public AbstractServer server; public NetManager netManager; public AbstractClient client; public static boolean hasClient; public InputMultiplexer inputMulti; public static Application.ApplicationType platform; public static ServerType serverType; @Override public void create () { Log.setLevel(Log.DEBUG); platform = Gdx.app.getType(); // we can pretend to be Android while running on desktop to test mobile features // such as mobile-specific input //platform = Application.ApplicationType.Android; Assets assets = new Assets(); assets.loadAll(); Log.debug("finished loading assets"); physics = new Physics(); initializeSubModules(); frame = 0; inst = this; if (isClient()) { view = new View(); new Particles(); input = new Input(); inputMulti = new InputMultiplexer(); inputMulti.addProcessor(View.inst.hud.stage); inputMulti.addProcessor(input); if (isMobile()) { inputMulti.addProcessor(GestureHandler.createGestureHandler()); } Gdx.input.setInputProcessor(inputMulti); } LevelBuilder.createLevel(); setupNetwork(); } private void setupNetwork() { netManager = new NetManager(); if (isOnlineServer()) { server = new NetServer(); } else if (isLocalServer()) { server = new LocalServer(); } if (isClient()) { clientEventManager = new ClientEventManager(); if (isLocalServer()) { client = new LocalClient(); client.connectToServer(); } else { client = new NetClient(); } } if (!isOnlineServer() && !isClient()) { throw new GdxRuntimeException("isServer and isClient are both false! At least one must be true"); } } public static int frame; public static boolean pause = false; @Override public void render () { try { if (pause) { Tools.sleep(10); return; } frame++; if (isServer()) { server.update(); } if (isClient()) { clientEventManager.process(); view.render(); input.process(); client.update(); } updateWorld(); } catch (Exception e) { if (!isHeadless()) { Gdx.input.setCursorCatched(false); } throw new GdxRuntimeException(e); } } private float accumulatedPhysicsTime; private long lastPhysicsTime = -1; public void updateWorld() { long start = TimeUtils.millis(); if (lastPhysicsTime == -1) { lastPhysicsTime = start; return; } float delta = (start - lastPhysicsTime) / 1000f; lastPhysicsTime = start; accumulatedPhysicsTime += delta; //Log.debug("physics delta: " + delta); //Log.debug("accumualted physics time: " + accumulatedPhysicsTime); while (accumulatedPhysicsTime >= Physics.TIME_STEP) { physics.run(); accumulatedPhysicsTime -= Physics.TIME_STEP; Main.updateSubModules(Physics.TIME_SCALE); Entity.updateAll(Physics.TIME_SCALE); } physicsTime = (int) TimeUtils.timeSinceMillis(start); } public static int physicsTime; @Override public void resize(int width, int height) { if (isClient()) { View.inst.storeSize(); } } // TODO check around to make sure all things are getting disposed, in all classes // bullet stuff, models, etc @Override public void dispose() { if (View.inst != null) { View.inst.dispose(); } Assets.inst.dispose(); LevelBuilder.dispose(); Movement.disposeAll(); physics.dispose(); } private void initializeSubModules() { // lots of static objects are assigned at startup // this saves us the pain of Android keeping static objects alive // after app closure Log.init(); Pooler.init(); if (isClient()) { Toggleable.init(); } LevelBuilder.init(); Block.init(); if (isClient()) { Shadow.init(); Sky.init(); } Entity.init(); Movement.init(); } public static void updateSubModules(float timeStep) { BulletHit.updateAll(timeStep); } public static boolean isClient() { return hasClient; } public static boolean isOnlineServer() { return serverType == ServerType.Online; } public static boolean isLocalServer() { return serverType == ServerType.Local; } public static boolean isServer() { return isOnlineServer() || isLocalServer(); } public static boolean isMobile() { return isAndroid() || isIOS(); } public static boolean isAndroid() { return platform == Application.ApplicationType.Android; } public static boolean isIOS() { return platform == Application.ApplicationType.iOS; } public static boolean isDesktop() { return platform == Application.ApplicationType.Desktop; } public static boolean isHeadless() { return platform == Application.ApplicationType.HeadlessDesktop; } public static NetClient getNetClient() { return (NetClient) inst.client; } public static NetServer getNetServer() { return (NetServer) inst.server; } }