/*
* Aphelion
* Copyright (c) 2013 Joris van der Wel
*
* This file is part of Aphelion
*
* Aphelion is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* Aphelion is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Aphelion. If not, see <http://www.gnu.org/licenses/>.
*
* In addition, the following supplemental terms apply, based on section 7 of
* the GNU Affero General Public License (version 3):
* a) Preservation of all legal notices and author attributions
* b) Prohibition of misrepresentation of the origin of this material, and
* modified versions are required to be marked in reasonable ways as
* different from the original version (for example by appending a copyright notice).
*
* Linking this library statically or dynamically with other modules is making a
* combined work based on this library. Thus, the terms and conditions of the
* GNU Affero General Public License cover the whole combination.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent modules,
* and to copy and distribute the resulting executable under terms of your
* choice, provided that you also meet, for each linked independent module,
* the terms and conditions of the license of that module. An independent
* module is a module which is not derived from or based on this library.
*/
package aphelion.shared.physics.operations;
import aphelion.shared.physics.SimpleEnvironment;
import aphelion.shared.physics.operations.pub.OperationPublic;
import aphelion.shared.physics.State;
import aphelion.shared.swissarmyknife.LinkedListEntry;
/**
*
* @author Joris
*/
public abstract class Operation implements OperationPublic
{
public final OperationKey key;
public final SimpleEnvironment env;
public final LinkedListEntry<Operation>[] link; // key is state_id. This is used in the linkedlist for PhysicsState.todo and PhysicsState.history
public long tick;
public int pid = 0; // 0 means this operation does not belong to a player
public final boolean ignorable; // if set, the operation may be dropped if too old
protected final int priority; // If there are multiple operation within the same tick.
// Operations with the lowest priority are executed first
public static enum PRIORITY
{
UNLOAD_CONFIG(0),
LOAD_CONFIG(1),
ACTOR_NEW(10),
ACTOR_MODIFICATION(11),
ACTOR_REMOVE(12),
ACTOR_WARP(20),
ACTOR_MOVE(21),
ACTOR_WEAPON_FIRE(30),
ACTOR_WEAPON_SYNC(30),
ACTOR_SYNC(1000), // see isLateSyncOperation() and state.tickOperations()
;
public int priority;
private PRIORITY(int priority)
{
this.priority = priority;
}
};
public boolean isLateSyncOperation()
{
return false;
}
@SuppressWarnings("unchecked")
protected Operation(SimpleEnvironment env, boolean ignorable, PRIORITY priority, OperationKey key)
{
int i;
this.key = key;
this.env = env;
this.ignorable = ignorable;
this.priority = priority.priority;
link = new LinkedListEntry[env.econfig.TRAILING_STATES];
for (i = 0; i < env.econfig.TRAILING_STATES; i++)
{
link[i] = new LinkedListEntry<>(this);
}
}
@Override
public String toString()
{
return this.getClass().getSimpleName() + ": " + tick;
}
/** This is used for sorting in the state todo list.
* Subclasses should override this method to make sure operations
* of the same type and the same tick are executed in the correct
* order on every machine.
* @param other
* @return < 0 if the priority of this operation is less than the argument "other",
* in this case this operation will be executed sooner.
* 0 for equal, > 0 if this operation has a higher priority.
*/
public int comparePriority(Operation other)
{
int c = Integer.compare(this.priority, other.priority);
if (c == 0)
{
c = Integer.compare(this.pid, other.pid);
}
return c;
}
/**
* Execute the operation on a state.
*
* Execution should perform the following steps:
*
* 1. Verify this operation is allowed at this time (ex: do not allow the player fire while dead, player does no
* have enough energy to fire, etc).
* 2. Execute operation.
* 3. Store the end result of all changes (like the new
* position of the player in a move operation). To be used in isConsistent()
* AND/OR:
* 3. Immediately check if the execution was consistent with newer states and return false if it is not.
*
* Return value vs isConsistent:
* The main difference is that isConsistent() is called after all other work for this tick is done.
* Thus using "return false" is not suitable in all situations.
*
* @param state
* @param late If false, this operation is being executed as part of the State.tickOperations() method,
* because it is on the todo list.
*
* If true, the state is not currently ticking and the operation has arrived late. Some operations
* may want to perform basic corrections. Trailing state synchronization will correct bigger
* inconsistencies. Note that late=true,ticks_late=0 is a valid combination.
* @param ticks_late How many ticks this operation is performed late.
* @return return false to indicate the execution of this operation was not consistent with newer states.
* A time warp to _this_ state will occur.
*/
abstract public boolean execute(State state, boolean late, long ticks_late);
/** Has this operation been executed consistently between the given two states?.
* Compare the stored results from step 3 in execute() with each other.
* Depending on the type of operation, this check does not have to be strongly
* consistent. For example a MOVE operation may allow a few pixels of deviation.
* (other more important events such as weapon hit may resolve such errors later).
* @param older newer.tick > older.tick
* @param newer newer.tick > older.tick
* @return false if no longer consistent and a timewarp should occur
*/
abstract public boolean isConsistent(State older, State newer);
/** Called during a timewarp to reset the history state of this operation.
*
* @param state
* @param resetTo The state to reset to
* @param resetToOperation The foreign operation to reset to.
* If resetTo is a local state (as opposed to foreign),
* it is the same as "this".
*/
abstract public void resetExecutionHistory(State state, State resetTo, Operation resetToOperation);
/** Called during a timewarp if this operation is placed back on the todo list for a state. */
abstract public void placedBackOnTodo(State state);
@Override
public long getTick()
{
return tick;
}
@Override
public int getPid()
{
return pid;
}
}