public SpoutEntity( SpoutEngine engine, Transform transform, Controller controller, int viewDistance, UUID uid, boolean load) { id.set(NOTSPAWNEDID); this.transform.set(transform); if (uid != null) { this.uid = uid; } else { this.uid = UUID.randomUUID(); } chunkLive = new AtomicReference<Chunk>(); entityManagerLive = new AtomicReference<EntityManager>(); controllerLive = new AtomicReference<Controller>(); if (transform != null && load) { chunkLive.set(transform.getPosition().getWorld().getChunkFromBlock(transform.getPosition())); entityManagerLive.set(((SpoutRegion) chunkLive.get().getRegion()).getEntityManager()); } viewDistanceLive.set(viewDistance); controllerLive.set(controller); if (controller != null) { controller.attachToEntity(this); if (controller instanceof PlayerController) { setObserver(true); } } }
@Override public void updateView() { Transform transform = ((PredictableTransformComponent) getOwner().getTransform()).getRenderTransform(); if (transform != null) { Matrix pos = MathHelper.translate(transform.getPosition().multiply(-1)); Matrix rot = MathHelper.rotate(transform.getRotation()); view = pos.multiply(rot); frustum.update(projection, view, transform.getPosition()); } }
private void updateObserver() { // Player view distance is handled in the network synchronizer if (controllerLive.get() instanceof PlayerController) { return; } int viewDistance = (getViewDistance() + 15) / Chunk.CHUNK_SIZE; // round up World w = transform.getPosition().getWorld(); int cx = chunkLive.get().getX(); int cy = chunkLive.get().getY(); int cz = chunkLive.get().getZ(); HashSet<SpoutChunk> observing = new HashSet<SpoutChunk>(viewDistance * viewDistance * viewDistance); for (int dx = -viewDistance; dx < viewDistance; dx++) { for (int dy = -viewDistance; dy < viewDistance; dy++) { for (int dz = -viewDistance; dz < viewDistance; dz++) { Chunk chunk = w.getChunk(cx + dx, cy + dy, cz + dz, true); chunk.refreshObserver(this); observing.add((SpoutChunk) chunk); } } } observingChunks.removeAll(observing); for (SpoutChunk chunk : observingChunks) { if (chunk.isLoaded()) { chunk.removeObserver(this); } } observingChunks.clear(); observingChunks.addAll(observing); }
@Override public void onTick(float dt) { Controller cont = controllerLive.get(); // Pulse all player messages here, so they can interact with the entities position safely if (cont instanceof PlayerController) { Player player = ((PlayerController) cont).getPlayer(); if (player != null && player.getSession() != null) { ((SpoutSession) player.getSession()).pulse(); } } // Tick the controller if (cont != null) { // Sanity check if (cont.getParent() != this) { if (Spout.debugMode()) { throw new IllegalStateException("Controller parent does not match entity"); } else { cont.attachToEntity(this); } } // If this is the first tick, we need to attach the controller // Controller is attached here instead of inside of the constructor // because the constructor can not access getChunk if the entity is being deserialized if (!attached) { cont.onAttached(); attached = true; } cont.onTick(dt); } // Copy values last (position may change during controller or session pulses) if (!isDead() && this.transform.getPosition() != null && this.transform.getPosition().getWorld() != null) { // Note: if the chunk is null, this effectively kills the entity (since dead: {chunkLive.get() // == null}) chunkLive.set( transform.getPosition().getWorld().getChunkFromBlock(transform.getPosition(), false)); entityManagerLive.set(((SpoutRegion) getRegion()).getEntityManager()); lastTransform = transform.copy(); } }
@Override public BlockIterator getAlignedBlocks() { Player player = (Player) getOwner(); Transform ptr = player.get(PlayerHead.class).getHeadTransform(); Transform tr = new Transform(); tr.setRotation( QuaternionMath.rotationTo(Vector3.FORWARD, ptr.getRotation().getDirection().multiply(-1))); tr.setPosition(ptr.getPosition()); return new BlockIterator(player.getWorld(), tr, getRange()); }
@DelayedWrite public boolean connect(SpoutSession<?> session, Transform newTransform) { if (!onlineLive.compareAndSet(false, true)) { // player was already online return false; } // Disallow null transforms or transforms with null worlds if (newTransform == null || newTransform.getPosition().getWorld() == null) { return false; } getTransform().setTransform(newTransform); setupInitialChunk(newTransform); sessionLive.set(session); copySnapshot(); return true; }
@Override public Transform convert(Transform t) { return new Transform(convert(t.getPosition()), t.getRotation(), t.getScale()); }
@Override public List<Message> getUpdateMessages( Entity entity, Transform liveTransform, RepositionManager rm, boolean force) { // Movement final Transform prevTransform = rm.convert(entity.getScene().getTransform()); final Transform newTransform = rm.convert(liveTransform); final boolean looked = entity.getScene().isRotationDirty(); final int lastX = protocolifyPosition(prevTransform.getPosition().getX()); final int lastY = protocolifyPosition(prevTransform.getPosition().getY()); final int lastZ = protocolifyPosition(prevTransform.getPosition().getZ()); final int lastYaw = protocolifyYaw(prevTransform.getRotation().getYaw()); final int lastPitch = protocolifyPitch(prevTransform.getRotation().getPitch()); final int newX = protocolifyPosition(newTransform.getPosition().getX()); final int newY = protocolifyPosition(newTransform.getPosition().getY()); final int newZ = protocolifyPosition(newTransform.getPosition().getZ()); final int newYaw = protocolifyYaw(newTransform.getRotation().getYaw()); final int newPitch = protocolifyPitch(newTransform.getRotation().getPitch()); final int deltaX = newX - lastX; final int deltaY = newY - lastY; final int deltaZ = newZ - lastZ; final int deltaYaw = newYaw - lastYaw; final int deltaPitch = newPitch - lastPitch; final List<Message> messages = new ArrayList<Message>(); /* * Two scenarios: * - The entity moves more than 4 blocks and maybe changes rotation. * - The entity moves less than 4 blocks and maybe changes rotation. */ if (force || deltaX > 128 || deltaX < -128 || deltaY > 128 || deltaY < -128 || deltaZ > 128 || deltaZ < -128) { messages.add(new EntityTeleportMessage(entity.getId(), newX, newY, newZ, newYaw, newPitch)); if (force || looked) { messages.add(new EntityYawMessage(entity.getId(), newYaw, newPitch)); } } else if (deltaX != 0 || deltaY != 0 || deltaZ != 0 || deltaYaw != 0 || deltaPitch != 0) { if (looked) { messages.add( new EntityRelativePositionYawMessage( entity.getId(), deltaX, deltaY, deltaZ, newYaw, newPitch)); } else if (!prevTransform.getPosition().equals(newTransform.getPosition())) { messages.add(new EntityRelativePositionMessage(entity.getId(), deltaX, deltaY, deltaZ)); } } // Head movement HeadComponent head = entity.get(HeadComponent.class); if (head != null && head.isDirty()) { final int headYawProt = ChannelBufferUtils.protocolifyYaw(head.getRotation().getYaw()); messages.add(new EntityHeadYawMessage(entity.getId(), headYawProt)); } // Physics // TODO: Actually not used? /*if (physics != null && physics.isLinearVelocityDirty()) { messages.add(new EntityVelocityMessage(entity.getId(), new Vector3(0, 0, 0))); }*/ // Extra metadata List<Parameter<?>> params = getUpdateParameters(entity); if (lastMeta == null || !lastMeta.equals(params)) { messages.add(new EntityMetadataMessage(entity.getId(), params)); lastMeta = params; } return messages; }
@Override public World getWorld() { return transform.getPosition().getWorld(); }
@Override public Point getPosition() { return transform.getPosition(); }