private boolean isReplay() { boolean isReplay = false; if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.REPLAY_FINISHED || recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.REPLAYING) { isReplay = true; if (recordAndReplayUtils.isShutdownRequested()) { recordAndReplayCurrentStatus.setStatus(RecordAndReplayStatus.NOT_ACTIVATED); recordAndReplayUtils.reset(); } } return isReplay; }
@Override public boolean step() { //Activate record when the preparations are ready if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.PREPARING_RECORD) { recordAndReplayCurrentStatus.setStatus(RecordAndReplayStatus.RECORDING); } //Activate the replay when the preparations are ready if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.PREPARING_REPLAY) { recordAndReplaySerializer.deserializeRecordAndReplayData(); recordAndReplayCurrentStatus.setStatus(RecordAndReplayStatus.REPLAYING); } return true; }
/** * Calls the 'process' method if the replay is activated and the event is of a type selected to be replayed. * This way, events of the types that are recorded and replayed are ignored during a replay. This is what makes * the player have no control over the character during a replay. * @param entity the entity which the event was sent against. * @param event the event being sent. */ @Override public void send(EntityRef entity, Event event) { if (recordAndReplayCurrentStatus.getStatus() != RecordAndReplayStatus.REPLAYING || !isSelectedToReplayEvent(event)) { originalSend(entity, event); } }
/** * Calls the 'process' method if the replay is activated and the event is of a type selected to be replayed. * This way, events of the types that are recorded and replayed are ignored during a replay. This is what makes * the player have no control over the character during a replay. * @param entity the entity which the event was sent against. * @param event the event being sent. * @param component the component sent along with the event. */ @Override public void send(EntityRef entity, Event event, Component component) { if (recordAndReplayCurrentStatus.getStatus() != RecordAndReplayStatus.REPLAYING || !isSelectedToReplayEvent(event)) { originalSend(entity, event, component); } }
@Override public void send(EntityRef entity, Event event, Component component) { if (Thread.currentThread() != mainThread) { pendingEvents.offer(new PendingEvent(entity, event, component)); } else { if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.RECORDING) { eventCatcher.addEvent(new PendingEvent(entity, event, component)); } SetMultimap<Class<? extends Component>, EventHandlerInfo> handlers = componentSpecificHandlers.get(event.getClass()); if (handlers != null) { List<EventHandlerInfo> eventHandlers = Lists.newArrayList(handlers.get(component.getClass())); eventHandlers.sort(priorityComparator); for (EventHandlerInfo eventHandler : eventHandlers) { if (eventHandler.isValidFor(entity)) { eventHandler.invoke(entity, event); } } } } }
/** * Processes recorded events for a certain amount of time and only if the timestamp is right. */ private void processRecordedEvents() { if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.REPLAYING && !this.areRecordedEventsLoaded) { initialiseReplayData(); } //If replay is ready, process some recorded events if the time is right. if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.REPLAYING) { processRecordedEventsBatch(1); if (this.recordedEvents.isEmpty()) { if (recordAndReplayUtils.getFileCount() <= recordAndReplayUtils.getFileAmount()) { //Get next recorded events file loadNextRecordedEventFile(); } else { finishReplay(); } } } }
@Override public void send(EntityRef entity, Event event) { if (Thread.currentThread() != mainThread) { pendingEvents.offer(new PendingEvent(entity, event)); } else { if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.RECORDING) { eventCatcher.addEvent(new PendingEvent(entity, event)); } networkReplicate(entity, event); Set<EventHandlerInfo> selectedHandlersSet = selectEventHandlers(event.getClass(), entity); List<EventHandlerInfo> selectedHandlers = Lists.newArrayList(selectedHandlersSet); selectedHandlers.sort(priorityComparator); if (event instanceof ConsumableEvent) { sendConsumableEvent(entity, event, selectedHandlers); } else { sendStandardEvent(entity, event, selectedHandlers); } } }
@Override public void finishSavingAndShutdown() { if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.RECORDING) { recordAndReplayUtils.setShutdownRequested(true); } saveThreadManager.shutdown(new ShutdownTask(), true); checkSaveTransactionAndClearUpIfItIsDone(); }
@Test public void testReplayStatus() { assertEquals(RecordAndReplayStatus.REPLAYING, recordAndReplayCurrentStatus.getStatus()); long startTime = System.currentTimeMillis(); while ((System.currentTimeMillis() - startTime) < 30) { eventSystem.process(); } assertEquals(RecordAndReplayStatus.REPLAY_FINISHED, recordAndReplayCurrentStatus.getStatus()); }
@ReceiveEvent(components = {CharacterMovementComponent.class, LocationComponent.class, AliveCharacterComponent.class}) public void onPlayerInput(CharacterMoveInputEvent input, EntityRef entity) { CharacterCollider characterCollider = physics.getCharacterCollider(entity); if (characterCollider.isPending()) { logger.debug("Skipping input, collision not yet established"); return; } CircularBuffer<CharacterStateEvent> stateBuffer = characterStates.get(entity); CharacterStateEvent lastState = stateBuffer.getLast(); float delta = input.getDeltaMs() + lastState.getTime() - (time.getGameTimeInMs() + MAX_INPUT_OVERFLOW ); if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.REPLAYING) { delta -= MAX_INPUT_OVERFLOW_REPLAY_INCREASE; } if (delta < 0) { CharacterStateEvent newState = stepState(input, lastState, entity); stateBuffer.add(newState); if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.REPLAYING) { characterStateEventPositionMap.updateCharacterStateEvent(newState); } else if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.RECORDING) { characterStateEventPositionMap.add(newState.getSequenceNumber(), newState.getPosition(), newState.getVelocity()); } characterMovementSystemUtility.setToState(entity, newState); lastInputEvent.put(entity, input); } else { logger.warn("Received too much input from {}, dropping input. Delta difference: {}", entity, delta); } }
private void saveRecordingData() { if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.RECORDING) { if (recordAndReplayUtils.isShutdownRequested()) { recordAndReplaySerializer.serializeRecordAndReplayData(); recordAndReplayCurrentStatus.setStatus(RecordAndReplayStatus.NOT_ACTIVATED); recordAndReplayUtils.reset(); } else { String recordingPath = PathManager.getInstance().getRecordingPath(recordAndReplayUtils.getGameTitle()).toString(); recordAndReplaySerializer.serializeRecordedEvents(recordingPath); } } }
private void waitForCompletionOfPreviousSave() { if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.REPLAY_FINISHED) { recordAndReplayUtils.setShutdownRequested(true); //Important to trigger complete serialization in a recording } if (saveTransaction != null && saveTransaction.getResult() == null) { saveThreadManager.shutdown(new ShutdownTask(), true); saveThreadManager.restart(); } checkSaveTransactionAndClearUpIfItIsDone(); }
private static EventSystem createEventSystem(NetworkSystem networkSystem, PojoEntityManager entityManager, EntitySystemLibrary library, RecordedEventStore recordedEventStore, RecordAndReplaySerializer recordAndReplaySerializer, RecordAndReplayUtils recordAndReplayUtils, RecordAndReplayCurrentStatus recordAndReplayCurrentStatus) { EventSystem eventSystem; List<Class<?>> selectedClassesToRecord = createSelectedClassesToRecordList(); if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.PREPARING_REPLAY) { eventSystem = new EventSystemReplayImpl(library.getEventLibrary(), networkSystem, entityManager, recordedEventStore, recordAndReplaySerializer, recordAndReplayUtils, selectedClassesToRecord, recordAndReplayCurrentStatus); } else { EventCatcher eventCatcher = new EventCatcher(selectedClassesToRecord, recordedEventStore); eventSystem = new EventSystemImpl(library.getEventLibrary(), networkSystem, eventCatcher, recordAndReplayCurrentStatus); } return eventSystem; }
private Path getSaveOrRecordingPath() { Path saveOrRecordingPath; if (context.get(RecordAndReplayCurrentStatus.class).getStatus() == RecordAndReplayStatus.PREPARING_REPLAY) { saveOrRecordingPath = PathManager.getInstance().getRecordingPath(gameManifest.getTitle()); } else { saveOrRecordingPath = PathManager.getInstance().getSavePath(gameManifest.getTitle()); } return saveOrRecordingPath; }
private void startSaving() { logger.info("Saving - Creating game snapshot"); PerformanceMonitor.startActivity("Saving"); ComponentSystemManager componentSystemManager = CoreRegistry.get(ComponentSystemManager.class); for (ComponentSystem sys : componentSystemManager.iterateAll()) { sys.preSave(); } saveRequested = false; saveTransaction = createSaveTransaction(); saveThreadManager.offer(saveTransaction); if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.NOT_ACTIVATED) { saveGamePreviewImage(); } for (ComponentSystem sys : componentSystemManager.iterateAll()) { sys.postSave(); } PerformanceMonitor.endActivity(); entitySetDeltaRecorder = new EntitySetDeltaRecorder(this.entityRefReplacingComponentLibrary); logger.info("Saving - Snapshot created: Writing phase starts"); }
@ReceiveEvent(components = LocationComponent.class, netFilter = RegisterMode.AUTHORITY) public void onAttackRequest(AttackRequest event, EntityRef character, CharacterComponent characterComponent) { // if an item is used, make sure this entity is allowed to attack with it if (event.getItem().exists()) { if (!character.equals(event.getItem().getOwner())) { return; } } OnItemUseEvent onItemUseEvent = new OnItemUseEvent(); character.send(onItemUseEvent); if (!onItemUseEvent.isConsumed()) { EntityRef gazeEntity = GazeAuthoritySystem.getGazeEntityForCharacter(character); LocationComponent gazeLocation = gazeEntity.getComponent(LocationComponent.class); Vector3f direction = gazeLocation.getWorldDirection(); Vector3f originPos = gazeLocation.getWorldPosition(); if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.RECORDING) { directionAndOriginPosRecorderList.getAttackEventDirectionAndOriginPosRecorder().add(direction, originPos); } else if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.REPLAYING) { Vector3f[] data = directionAndOriginPosRecorderList.getAttackEventDirectionAndOriginPosRecorder().poll(); direction = data[0]; originPos = data[1]; } HitResult result = physics.rayTrace(originPos, direction, characterComponent.interactionRange, Sets.newHashSet(character), DEFAULTPHYSICSFILTER); if (result.isHit()) { result.getEntity().send(new AttackEvent(character, event.getItem())); } } }
Vector3f direction = getViewDirection(); Vector3f originPos = getViewPosition(); if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.RECORDING) { this.directionAndOriginPosRecorderList.getTargetOrOwnedEntityDirectionAndOriginPosRecorder().add(direction, originPos); } else if (recordAndReplayCurrentStatus.getStatus() == RecordAndReplayStatus.REPLAYING) { Vector3f[] data = this.directionAndOriginPosRecorderList.getTargetOrOwnedEntityDirectionAndOriginPosRecorder().poll(); direction = data[0];