Index: geniuswebcore/geniusweb/protocol/NegoSettings.py
===================================================================
--- geniuswebcore/geniusweb/protocol/NegoSettings.py	(revision 67)
+++ geniuswebcore/geniusweb/protocol/NegoSettings.py	(revision 69)
@@ -8,5 +8,6 @@
 
 
-@JsonSubTypes( ["geniusweb.protocol.session.saop.SAOPSettings.SAOPSettings" ])
+@JsonSubTypes( ["geniusweb.protocol.session.saop.SAOPSettings.SAOPSettings",
+			"geniusweb.protocol.session.mopac.MOPACSettings.MOPACSettings" ])
 @JsonTypeInfo(use = Id.NAME, include = As.WRAPPER_OBJECT)
 class NegoSettings(ABC):
Index: geniuswebcore/geniusweb/protocol/session/mopac/MOPAC.py
===================================================================
--- geniuswebcore/geniusweb/protocol/session/mopac/MOPAC.py	(revision 69)
+++ geniuswebcore/geniusweb/protocol/session/mopac/MOPAC.py	(revision 69)
@@ -0,0 +1,332 @@
+import logging
+from threading import Timer
+import threading
+from time import time, sleep
+from typing import Optional, List
+
+from tudelft.utilities.listener.DefaultListenable import DefaultListenable
+from tudelft.utilities.listener.Listener import Listener
+from tudelft.utilities.repository.NoResourcesNowException import NoResourcesNowException
+from tudelft_utilities_logging.Reporter import Reporter
+from uri.uri import URI
+
+from geniusweb.actions.Action import Action
+from geniusweb.events.ProtocolEvent import ProtocolEvent
+from geniusweb.inform.Finished import Finished
+from geniusweb.inform.Settings import Settings
+from geniusweb.progress.ProgressFactory import ProgressFactory
+from geniusweb.protocol.CurrentNegoState import CurrentNegoState
+from geniusweb.protocol.ProtocolException import ProtocolException
+from geniusweb.protocol.partyconnection.ProtocolToPartyConn import ProtocolToPartyConn
+from geniusweb.protocol.partyconnection.ProtocolToPartyConnFactory import ProtocolToPartyConnFactory
+from geniusweb.protocol.partyconnection.ProtocolToPartyConnections import ProtocolToPartyConnections
+from geniusweb.protocol.session.SessionProtocol import SessionProtocol
+from geniusweb.protocol.session.mopac.MOPACState import MOPACState
+from geniusweb.references.PartyWithProfile import PartyWithProfile
+from geniusweb.references.ProtocolRef import ProtocolRef
+from geniusweb.references.Reference import Reference
+from geniusweb.utils import val
+
+
+MINDURATION = 100
+MIN_SLEEP_TIME = 1000
+MAX_SLEEP_TIME = 60000
+MOPACPROTOCOL = ProtocolRef(URI("MOPAC"))
+
+
+class MOPAC (DefaultListenable[ProtocolEvent], SessionProtocol):
+	'''
+	All parties are first sent the {@link SessionSettings}. Only parties
+	specified initially in the settings do participate (no late joining in)
+	
+	<h2>parameter</h2> MOPAC parties can receive a parameter <code>power</code>
+	containing a Integer. This parameter is picked up by this protocol, for
+	handling the agreement extraction from the offers. Power=1 for parties that
+	do not have the power parameter.
+	<h2>Protocol steps</h2>
+	
+	<ol>
+	<li>The protocol tries to start all parties. If not all parties start, the
+	parties are freed up and another attempt is done to start all parties some
+	time later.
+	<li>The session deadline clock now starts ticking.
+	<li>remainingparties are sent their settings.
+	<li>Loop until {@link Deadline} is reached or |remainingparties| &le; 2:
+	<ol>
+	<li>protocol sends {@link YourTurn} to all remainingparties. Each party now
+	must submit an {@link Offer} within {@link Phase#PHASE_MAXTIME} seconds. If a
+	party fails to submit it is removed from from remainingparties.
+	<li>protocol sends a {@link Voting} containing a List of Bid containing all
+	received {@link Bid}s. Each party must place his {@link Votes} within the
+	provided deadline. If a party does not submit, it is terminated and removed
+	from remainingparties. Previous votes for the same bid do not count. But see
+	{@link Agreements}. A party that misbehaves after submitting its vote is
+	removed but it Votes remain standing.
+	<li>protocol sends to all remainingparties a OptIn containing all received
+	votes. Each party now must submit again a {@link Votes} object within the
+	deadline. This new Votes must equal or extend the previous Votes of the
+	party.
+	<li>The protocol uses the {@link Phase#getEvaluator()} to determine the
+	agreements from the votes. If there are agreements, the parties that placed
+	these votes reached an agreement. They are moved to the agreement set and
+	removed from the remainingparties.
+	</ol>
+	<li>Any remaining parties are sent a {@link Finished} object without
+	agreement bid and are terminated.
+	</ol>
+	<p>
+	A party can send {@link EndNegotiation} at any time to remove itself from the
+	negotiation. The other parties may continue anyawy without him.
+	<p>
+	Parties receive the {@link Finished} information at the very end when all
+	agreements have been collected.
+	<p>
+	This logs to "Protocol" logger if there are issues
+	<p>
+	This object is mutable: the internal state changes as parties interact with
+	the protocol.
+	<p>
+	Threadsafe: all entrypoints are synhronized.
+	'''
+	# Tech notes. 0. Everything here is phase driven and runs due to callback
+	# from parties. 1. Connect all parties. 2. If any party fails at any time,
+	# add it to exceptions list in the state 3. Run timer to check the phase at
+	# possible end time. The timer just checks the state, and therefore it does
+	# no harm if it is double checked. 4. We run phases always with either
+	# PHASE_TIME or time to the global deadline, whichever comes first.
+	# Therefore phases naturally end at end of nego. 5. Phases must have at
+	# least {@link #MINDURATION} otherwise it makes no sense to even start it.
+
+	_state:Optional[MOPACState]  = None; # mutable!
+	
+	# the existing party connections. we assume ownership of this so it should
+	# not be modified although connections may of course break. mutable!
+	_connections:ProtocolToPartyConnections = ProtocolToPartyConnections([])
+
+	# Set to true after all is done: we sent final outcomes. This is needed
+	# because we can't tell from state what we did at the very end.
+	_finished = False
+	_timer = None
+
+	_synclock = threading.RLock()
+
+	def __init__(self, state:MOPACState , logger:Reporter ):
+		'''
+		@param state  normally the initial state coming from SAOPSettings
+		@param logger the {@link Reporter} to use
+		'''
+		super().__init__()
+		if state == None:
+			raise ValueError("state must be not null")
+		if state.getSettings().getDeadline().getDuration() < MINDURATION:
+			raise ValueError(
+					"Duration must be at least " + str(MINDURATION))
+		if logger == None :
+			raise ValueError("Logger must be not null")
+
+		self._log = logger
+		self._state = state
+
+	def start(self, connectionfactory:ProtocolToPartyConnFactory ) :
+		with self._synclock:
+			try:
+				self._connect(connectionfactory)
+			except Exception as e:
+				# We can't {@link #handleError} yet. FIXME 
+				raise ConnectionError("Failed to connect", e);
+			
+			now = int(1000*time()) 
+			self._state = val(self._state).initPhase(
+					ProgressFactory.create(val(self._state).getSettings().getDeadline(), now),
+					now);
+			self._setupParties()
+			self._startPhase(now)
+
+	def getDescription(self)->str:
+		return "All parties get YourTurn. They now can submit their Offer within 30 seconds. "\
+				+ "Next they receive a Voting. They can now submit their Votes."\
+				+ "Then they receive a a OptIn, now they can widen up their Votes."\
+				+ "If one of their Vote succeeds, they finish with an agreement. "\
+				+ "The VotingEvaluator setting determines the exact voting behaviour "\
+				+ "and if this process repeats"
+
+	def addParticipant(self, party:PartyWithProfile ):
+		raise ValueError("Dynamic joining a negotiation is not supported in AMOP")
+
+	def getState(self)->MOPACState :
+		return val(self._state)
+
+	def getRef(self)->ProtocolRef :
+		return MOPACPROTOCOL
+
+
+	#*******************************************************************
+	# private functions. Some are protected only, for testing purposes
+	# ********************************************************************/
+	def _connect(self, connectionfactory:ProtocolToPartyConnFactory ):
+		'''
+		step 1 in protocol: connect all involved parties and start the clock.
+		This always "succeeds" with a valid (but possibly final) state
+		<p>
+		This is 'protected' to allow junit testing, this code is not a 'public'
+		part of the interface.
+		
+		@param connectionfactory the connectionfactory for making party
+		         connections
+		
+		@throws InterruptedException if the connection procedure is unterrupted
+		
+		@throws IOException          if this fails to properly conect to the
+		             parties, eg interrupted or server not
+		             responding..
+		'''
+		with self._synclock:
+			participants = val(self._state).getSettings().getAllParties()
+			parties:List[Reference] = \
+				[parti.getParty().getPartyRef() for parti in participants]
+			self._log.log(logging.INFO, "MOPAC connect " + str(parties))
+			newconnections:Optional[List[ProtocolToPartyConn]]  = None
+
+			while newconnections == None: 
+				try:
+					newconnections = connectionfactory.connectAll(parties)
+				except NoResourcesNowException as e:
+					waitms = int(1000*e.getLater().timestamp()) - int(1000*time())
+					self._log.log(logging.INFO,
+							"No resources available to run session, waiting"
+									+ str(waitms))
+					sleep(min(MAX_SLEEP_TIME,
+							max(MIN_SLEEP_TIME, waitms))/1000.)
+
+			for i in range(len(participants)):
+				conn = val(newconnections)[i]
+				self._connections = self._connections.With(conn)
+				self._state = val(self._state).With(conn.getParty(), participants[i])
+
+	def _setupParties(self) :
+		'''
+		step 2 in protocol: listen to connections and send settings to the
+		parties.
+		<p>
+		This is 'protected' to allow junit testing, this code is not a 'public'
+		part of the interface.
+		'''
+		this=self
+		with self._synclock:
+			for conn in self._connections:
+				
+				class ActListener(Listener[Action]):
+					def __init__(self, conn):
+						self._conn=conn
+					def notifyChange(self, action: Action):
+				 		 this._actionRequest(self._conn, action, int(1000*time()))
+
+				conn.addListener(ActListener(conn))
+
+			for connection in self._connections:
+				try:
+					self._sendSettings(connection)
+				except ConnectionError as e:
+					self._state = val(self._state).WithException(ProtocolException("Failed to initialize",
+							connection.getParty()))
+
+	def _startPhase(self, now:int):
+		'''
+		Send the {@link Inform} for the current phase to the remaining parties
+		and start the time-out checker
+		'''
+		info = val(self._state).getPhase().getInform()
+		for pid in val(self._state).getPhase().getPartyStates().getNotYetActed() :
+			try:
+				val(self._connections.get(pid)).send(info)
+			except ConnectionError as e:
+				self._state = val(self._state).WithException(ProtocolException(
+						"Party seems to have disconnected", pid, e))
+		self._startTimer(1 + val(self._state).getPhase().getDeadline() - now)
+
+	def _startTimer(self, deadln:int):
+		'''
+		Check at given deadline if we already ended the phase.
+		
+		@param deadn the deadline (ms from now)
+		'''
+		if self._timer != None:
+			raise ValueError("Timer is still running!")
+		self._timer = Timer(deadln/1000.0, self._timertask)
+		self._timer.start()
+	
+	def _timertask(self):
+		self._checkEndPhase(int(1000*time()))
+
+	def _sendSettings(self, connection:ProtocolToPartyConn):
+		'''
+		Inform a party about its settings
+		
+		@param connection
+		@throws ConnectionError if party got disconnected
+		'''
+		with self._synclock:
+			partyid = connection.getParty()
+			profile = val(self._state).getPartyProfiles()[partyid].getProfile()
+			params = val(self._state).getPartyProfiles()[partyid].getParty().getParameters()
+			connection.send(Settings(connection.getParty(), profile,self.getRef(),
+					val(val(self._state).getProgress()), params));
+
+	def _actionRequest(self, partyconn:ProtocolToPartyConn , action:Action ,
+			now:int):
+		'''
+		This is called when one of the {@link ProtocolToPartyConn}s does an
+		action. Synchronized so that we always handle only 1 action at a time.
+		
+		@param partyconn the connection on which the action came in
+		@param action    the {@link Action} taken by some party
+		@param now       current time
+		'''
+		with self._synclock:
+			if self._finished:
+				return
+			self._state = val(self._state).WithAction(partyconn.getParty(), action, now)
+			self._checkEndPhase(int(1000*time()))
+
+	def _checkEndPhase(self, now:int) :
+		'''
+		The current phase may be completed. We check, because it may already been
+		handled. Proceed to next phase as needed. Reset the deadline timers and
+		inform parties. Increase progress if necessary. Must only be called
+		through {@link #endPhase} to ensure this is called only once.
+		'''
+		with self._synclock:
+			if not val(self._state).getPhase().isFinal(now):
+				return
+			# phase indeed ended. Check what's up.
+			if self._timer != None:
+				val(self._timer).cancel()
+				self._timer = None
+			self._state = val(self._state).finishPhase()
+			if self._state.isFinal(now):
+				print("state "+str(self._state)+" is final")
+				self._endNegotiation()
+				return
+	
+			self._state = self._state.nextPhase(now)
+	
+			self._startPhase(now)
+
+	def _endNegotiation(self):
+		'''
+		To be called when we reach final state. Must only be called if
+		{@link MOPACState#isFinal(long)}. Send finished info to all parties.
+		Double calls are automatically ignored using the global finished flag.
+		'''
+		with self._synclock:
+			if self._finished:
+				return
+			self._finished = True
+			info = Finished(self._state.getAgreements())
+			for conn in self._connections:
+				try:
+					conn.send(info)
+					conn.close()
+				except Exception as  e:
+					self._log.log(logging.INFO, "Failed to send Finished to " + str(conn), e)
+			self.notifyListeners(CurrentNegoState(self._state))
Index: geniuswebcore/geniusweb/protocol/session/mopac/MOPACSettings.py
===================================================================
--- geniuswebcore/geniusweb/protocol/session/mopac/MOPACSettings.py	(revision 69)
+++ geniuswebcore/geniusweb/protocol/session/mopac/MOPACSettings.py	(revision 69)
@@ -0,0 +1,118 @@
+from typing import List
+
+from tudelft_utilities_logging.Reporter import Reporter
+
+from geniusweb.deadline.Deadline import Deadline
+from geniusweb.protocol.session.SessionProtocol import SessionProtocol
+from geniusweb.protocol.session.SessionSettings import SessionSettings
+from geniusweb.protocol.session.TeamInfo import TeamInfo
+from geniusweb.protocol.tournament.Team import Team
+from geniusweb.references.PartyWithProfile import PartyWithProfile
+from geniusweb.voting.VotingEvaluator import VotingEvaluator
+
+
+class MOPACSettings (SessionSettings):
+	'''
+	Settings for MOPAC negotiation. in MOPAC, each party may get a "power"
+	parameter containing an natural number &le;1.
+	'''
+	
+	def __init__(self, participants:List[TeamInfo] ,
+			deadline:Deadline ,
+			 votingevaluator:VotingEvaluator):
+		'''
+		@param participants the list of {@link PartyWithProfile} in clockwise
+		            order. There must be at least 2 to run the MOPAC
+		            protocol. This is not tested in the constructor
+		            because this can be initialized with less, for use in
+		            TournamentSettings.
+		@param deadline     the {@link Deadline} for the negotiation
+		@param votingeval   the {@link VotingEvaluator} to use.
+		'''
+		self._participants = participants;
+		self._deadline = deadline;
+		if participants == None or deadline == None or votingevaluator == None:
+			raise ValueError(
+					"participants, deadline and votingeval must be not none")
+		self._votingevaluator = votingevaluator
+		self._checkTeams();
+
+	def getMaxRunTime(self)->float:
+		return self._deadline.getDuration() / 1000.
+
+	def getProtocol(self, logger:Reporter) -> SessionProtocol :
+		from geniusweb.protocol.session.mopac.MOPACState import MOPACState
+		from geniusweb.protocol.session.mopac.MOPAC import MOPAC
+
+		return  MOPAC(MOPACState(None, [], None, self, {}), logger) 
+
+	def getTeams(self ) -> List[TeamInfo] :
+		return list(self._participants)
+	
+	def getParticipants(self ) -> List[TeamInfo] :
+		'''
+		bit hacky, same as getTeams, for deserialization...
+		'''
+		return list(self._participants)
+
+	def getDeadline(self)-> Deadline :
+		'''
+		@return the deadline for this negotiation
+		'''
+		return self._deadline
+
+	def getAllParties(self)->List[PartyWithProfile] :
+		return [ particip.getParties()[0] for particip in self._participants]
+	
+
+	def getVotingEvaluator(self)->VotingEvaluator :
+		'''
+		@return a class that allows us to evaluate the voting results in
+		        different ways, selectable by the user.
+		'''
+		return self._votingevaluator
+
+	def With(self, team:TeamInfo ) -> "MOPACSettings" :
+		if team.getSize() != 1:
+			raise ValueError(
+					"Added party must have one party but got " + str(team))
+		newparts:List[TeamInfo]  = list(self._participants)
+		newparts.append(team)
+		return MOPACSettings(newparts, self._deadline, self._votingevaluator)
+
+	def __repr__(self)->str:
+		return "MOPACSettings[" + str(self._participants) + "," +\
+				str(self._deadline) + "," + \
+				type(self._votingevaluator).__name__ + "]";
+
+	def getTeamSize(self)->int:
+		return 1;
+
+	def __hash__(self):
+		return hash((tuple(self._participants), self._deadline, self._votingevaluator))
+	
+	def __eq__(self, other):
+		return isinstance(other, self.__class__)\
+			and self._participants == other._participants \
+			and self._deadline == other._deadline \
+			and self._votingevaluator == other._votingevaluator
+
+	def _checkTeams(self):
+		'''
+		@throws IllegalArgumentException if teams have improper power settings.
+		'''
+		for team in self._participants:
+			if team.getSize() != 1:
+				raise ValueError("All teams must be size 1 but found " + str(team))
+			party = team.getParties()[0]
+			if 'power' in party.getParty().getParameters().getParameters():
+				power = party.getParty().getParameters().get("power")
+				if not isinstance(power, int):
+					raise ValueError(
+							"parameter 'power' for party" + str(party)
+									+ " must be integer but found " + str(power))
+				if power < 1:
+					raise ValueError(
+							"parameter 'power'  for party" + str(party)
+									+ " must be >=1 but found " + str(power))
+
Index: geniuswebcore/geniusweb/protocol/session/mopac/MOPACState.py
===================================================================
--- geniuswebcore/geniusweb/protocol/session/mopac/MOPACState.py	(revision 69)
+++ geniuswebcore/geniusweb/protocol/session/mopac/MOPACState.py	(revision 69)
@@ -0,0 +1,281 @@
+from typing import List, Dict, Optional
+
+from geniusweb.actions.Action import Action
+from geniusweb.actions.PartyId import PartyId
+from geniusweb.inform.Agreements import Agreements
+from geniusweb.progress.Progress import Progress
+from geniusweb.progress.ProgressRounds import ProgressRounds
+from geniusweb.protocol.ProtocolException import ProtocolException
+from geniusweb.protocol.session.SessionResult import SessionResult
+from geniusweb.protocol.session.SessionState import SessionState
+from geniusweb.protocol.session.mopac.MOPACSettings import MOPACSettings
+from geniusweb.protocol.session.mopac.PartyStates import PartyStates
+from geniusweb.protocol.session.mopac.phase.OfferPhase import OfferPhase
+from geniusweb.protocol.session.mopac.phase.OptInPhase import OptInPhase
+from geniusweb.protocol.session.mopac.phase.Phase import Phase, PHASE_MAXTIME, \
+	PHASE_MINTIME
+from geniusweb.references.PartyWithProfile import PartyWithProfile
+from geniusweb.utils import val, toTuple
+
+
+class MOPACState (SessionState):
+	'''
+	Keeps track of the current {@link Phase}. Adds initializing stuff and
+	time/deadline checking. This state does not contain connections, this assumes
+	that someone else handles (i.e. {@link NegoProtocol} that and the connections
+	with the parties and that negotiation events are just pumped in from there.
+	<p>
+	This object is a bit tricky. It has two states
+	<ol>
+	<li>The initial state, where phase=null and connections are being made to all
+	parties. At this point, problems can not be handled nicely yet because there
+	are no PartyId's for problematic parties
+	<li>The working state, where all parties are connected and all problems can
+	be connected to an offending party.
+	</ol>	
+	'''
+
+# 	private final Phase phase; // maybe null while initializing
+# 	private final MOPACSettings settings;
+# 	private final Map<PartyId, PartyWithProfile> partyprofiles;
+# 	private final List<Action> actions;
+# 	private final Progress progress;
+# 
+# 	/**
+# 	 * Creates the initial state from the given settings and progress=null
+# 	 * 
+# 	 * @param settings the {@link SAOPSettings}
+# 	 */
+# 	public MOPACState(MOPACSettings settings) {
+# 		this(null, Arrays.asList(), null, settings, Collections.emptyMap());
+# 
+# 	}
+
+	def __init__(self, phase:Optional[Phase] ,
+			actions:List[Action] ,
+			progress:Optional[Progress] ,
+			settings:MOPACSettings,
+			partyprofiles:Dict[PartyId, PartyWithProfile] ): 
+		'''
+		@param phase         The Phase, or null if still initializing. Phase is
+			             set something only after we have connections.
+		@param actions       the legal actions that have been done in the
+			             negotiation. {@link PartyStates#getActions()} is
+			             called to collect the actions after the phase is
+			             finished.first action in list is the oldest. This
+			             will not contain actions that immediately led to an
+			             agreement, because {@link PartyStates} removes the
+			             actions that led to an agreement. This MUST NOT
+			             contain illegal actions. Parties doing illegal
+			             actions are killed and the offending action ends up
+			             in the stacktrace. Previous actions of crashed
+			             parties remain standing and valid.
+		@param progress      the {@link Progress} line. can be null if not yet
+			             known. null happens because during initialization
+			             phase the protocol first add all connections. During
+			             this time, parties may already enter the
+			             failed/exception state.
+		@param settings  the {@link SAOPSettings}
+		@param partyprofiles map with the {@link PartyWithProfile} for connected
+		    	         parties. null is equivalent to an empty map.
+		'''
+		self._phase = phase
+		self._actions = actions
+		self._progress = progress
+		self._settings = settings
+		self._partyprofiles = partyprofiles
+
+	def initPhase(self,newprogress:Progress , now:int) -> "MOPACState" :
+		'''
+		Sets the progress for this session and initial phase. Must be called
+		after all parties have been connected with
+		{@link #with(PartyId, PartyWithProfile)}.
+		
+		@param newprogress the initial {@link Progress} typically matching the
+		                   settings deadline object
+		@param now         current time ms since 1970
+		
+		@return state with the initial partystates , progress set.
+		'''
+		if self._progress != None or newprogress == None or self._phase != None:
+			raise ValueError(
+					"progress must be null, newprogress must be not null and phase must be INIT")
+
+		partyStates = PartyStates(self._getPowers())
+		firstPhase = OfferPhase(partyStates,
+				now + MOPACState._getAvailablePhaseTime(newprogress, now),
+				self._settings.getVotingEvaluator())
+		return MOPACState(firstPhase, self._actions, newprogress, self._settings,
+				self._partyprofiles)
+
+	def getActions(self)->List[Action]:
+		return list(self._actions)
+
+	def getProgress(self)->Optional[Progress] :
+		return self._progress
+
+	def getAgreements(self)-> Agreements: 
+		return val(self._phase).getPartyStates().getAgreements();
+
+	def getSettings(self)->MOPACSettings :
+		return self._settings
+
+	def getPartyProfiles(self)->Dict[PartyId, PartyWithProfile]:
+		return dict(self._partyprofiles)
+
+	def  isFinal(self, now:int)->bool:
+		return self._phase != None and val(self._phase).isFinal(now) \
+			and not self.isNewPhasePossible(now)
+
+	def getResult(self)->SessionResult :
+		return SessionResult(self._partyprofiles, self.getAgreements(),
+				{}, None)
+
+	@staticmethod
+	def  _getAvailablePhaseTime(aprogress:Progress , now:int) ->int: 
+		'''
+		@param progress the Progress that needs to be checked
+		@param now      current time ms since 1970
+		@return the max possible duration in ms of a phase considering the
+		        progress.
+		'''
+		return min(int(aprogress.getTerminationTime().timestamp()*1000) - now,
+				PHASE_MAXTIME)
+
+	def With(self, id:PartyId , partyprofile:PartyWithProfile ) -> "MOPACState" :
+		'''
+		@param id           the new {@link PartyId}
+		@param partyprofile the {@link PartyWithProfile} that is associated with
+		                    this state
+		@return new {@link MOPACState} with the new party added. This call
+		        ignores the progress (does not check isFinal) because we uses
+		        this during the setup where the deadline is not yet relevant.
+		'''
+		if self._phase != None:
+			raise ValueError(
+					"Adding connections only allowed while initializing");
+
+		newprofiles = dict(self._partyprofiles)
+		newprofiles[id]= partyprofile
+		return MOPACState(None, self._actions,self._progress, self._settings, newprofiles)
+	
+	def WithException(self, e:ProtocolException) ->"MOPACState" :
+		'''
+		@param e the {@link ProtocolException} that occured
+		@return a new state with the error set. You MUST have called
+		        {@link #initPhase(Progress, long)} before using this
+		'''
+		return MOPACState(val(self._phase).WithException(e), self._actions, self._progress, 
+						self._settings,self._partyprofiles)
+
+	def nextPhase(self, now:int)->"MOPACState" :
+		'''
+		Start the next phase. If new phase is OfferPhase, we increase progress.
+		Actions is reset to empty. does nothing if not
+		{@link #isNewPhasePossible(long)}
+		
+		@param now current time
+		@return new {@link MOPACState} with phase initialized for next phase.
+		'''
+		remainingNegoTime = int(val(self._progress).getTerminationTime().timestamp()*1000) - now
+		newphase = val(self._phase).next(now, min(remainingNegoTime, PHASE_MAXTIME))
+
+		return MOPACState(newphase, self._actions, 
+					MOPACState._increment(val(self._progress), val(self._phase)),
+				self.getSettings(), self._partyprofiles)
+
+	def isNewPhasePossible(self, now:int)->bool:
+		'''
+		When this is called, all parties should have acted.
+		
+		@param now current time
+		@return true if there are still &gt;2 parties active and we have enough
+		        time for a new phase.
+		'''
+		# System.out.println("phase=" + phase);
+		newprogress = MOPACState._increment(val(self._progress), val(self._phase))
+		if newprogress.isPastDeadline(now + PHASE_MINTIME):
+			return False
+
+		return len(val(self._phase).getPartyStates().getNegotiatingParties()) >= 2\
+				and self._getAvailablePhaseTime(newprogress,
+						now) > PHASE_MINTIME
+
+	def getPhase(self)->Phase :
+		return val(self._phase)
+
+	def WithAction(self, actor:PartyId,  action:Action, now:int)->"MOPACState" :
+		'''
+		Check if action is allowed. Add action to the list of actions. Notice,
+		this does NOT check if we need to step to the next phase, because
+		deciding that is also depending on time-outs.
+		
+		@param actor  the actor that did this action. Can be used to check if
+		              action is valid. NOTICE caller has to make sure the current
+		              state is not final. MUST NOT be null.
+		@param action the action that was proposed by actor. MUST NOT be null.
+		@param now    the current time in ms since 1970, see
+		              {@link System#currentTimeMillis()}
+		@return new {@link MOPACState} with the action checked and registered. If
+		        the action is not allowed, the new state may be that the actor is
+		        in the exception list.
+		'''
+		return MOPACState(val(self._phase).With(actor, action, now), self._actions, self._progress,
+				self._settings, self._partyprofiles)
+
+	@staticmethod
+	def _increment(aprogress:Progress , aphase:Phase ) ->Progress :
+		'''
+		@param aprogress the progress that might need to be advanced
+		@param aphase    the phase
+		@return the next progress. Progress round advances if phase is
+		        {@link OptIn}.
+		'''
+		if isinstance(aprogress,ProgressRounds) and isinstance(aphase,OptInPhase):
+			return aprogress.advance()
+		return aprogress
+
+	def _getPowers(self)-> Dict[PartyId, int]:
+		'''
+		@return the power of all parties as set in their parameters, default =1.
+		    bad power values (non-integer, or <1) are ignored.
+		'''
+		map:Dict[PartyId,int] = {}
+		for pid in self._partyprofiles:
+			if self._partyprofiles[pid].getParty().getParameters().containsKey("power"):
+				power = self._partyprofiles[pid].getParty().getParameters()\
+					.get("power")
+				if  not isinstance(power, int) or power < 1:
+					power = 1
+			else:
+				power=1
+			map[pid]=power
+		return map
+
+	def __repr__(self)->str:
+		return "MOPACState[" + str(self._phase) + "," + str(self._settings)\
+			+ "," + str(self._partyprofiles) + "," + str(self._progress) + "]"
+
+	def finishPhase(self)->"MOPACState" :
+		'''
+		@return a wrapped-up state, with all parties doen an action or kicked,
+		        and agreements collected
+		'''
+		newphase = val(self._phase).finish()
+		newactions = list(self._actions)
+		newactions=newactions+ newphase.getPartyStates().getActions()
+		return MOPACState(newphase, newactions, self._progress, self._settings,
+				self._partyprofiles)
+
+	def __hash__(self):
+		return hash((tuple(self._actions), toTuple(self._partyprofiles), 
+					self._phase, self._progress, self._settings))
+	
+	def __eq__(self, other):
+		return isinstance(other, self.__class__) \
+			 and self._actions==other._actions\
+			 and self._partyprofiles==other._partyprofiles\
+			 and self._phase==other._phase\
+			 and self._progress==other._progress\
+			 and self._settings==other._settings
+
Index: geniuswebcore/geniusweb/protocol/session/mopac/PartyStates.py
===================================================================
--- geniuswebcore/geniusweb/protocol/session/mopac/PartyStates.py	(revision 69)
+++ geniuswebcore/geniusweb/protocol/session/mopac/PartyStates.py	(revision 69)
@@ -0,0 +1,206 @@
+from typing import Set, List, Dict, TypeVar, Optional
+
+from geniusweb.actions.Action import Action
+from geniusweb.actions.EndNegotiation import EndNegotiation
+from geniusweb.actions.PartyId import PartyId
+from geniusweb.inform.Agreements import Agreements
+from geniusweb.protocol.ProtocolException import ProtocolException
+from geniusweb.utils import toTuple, val, toStr
+
+
+class PartyStates:
+	'''
+	Invariant: contains the current state of all participating parties. A party
+	either notYetActed, did action, reached an agreement, walked away or made a
+	{@link ProtocolException}.
+	<p>
+	After a phase completes, the actions are filtered/handled to collect
+	agreements and move remaining parties back to notYetActed.
+	'''
+	# all parties must be in exactly one state at all times.
+	
+	def __init__(self, powers:Dict[PartyId, int],
+			notYetActed:Optional[Set[PartyId]]=None,
+			actions: List[Action] =[],
+			agreements:Agreements = Agreements(),
+			walkedAway:List[PartyId]=[],
+			exceptions:Dict[PartyId, ProtocolException] ={}):
+		'''
+		notice: order of arguments, power is now first instead of last because
+		python requires obligatory arguments first.
+		'''
+		if notYetActed!=None:
+			self._notYetActed = set(notYetActed) #type:ignore
+		else:
+			self._notYetActed = set(powers.keys())
+		self._actions = list(actions)
+		self._agreements = agreements
+		self._walkedAway = list(walkedAway)
+		self._exceptions = dict(exceptions)
+		self._powers = dict(powers)
+
+
+
+	def WithAction(self,  action:Action)-> "PartyStates":
+		'''
+		@param action the action done by some party. The correctness of action,
+		              particularly of {@link Action#getActor()}, must be correct.
+		@return new state with party done given action. This just accepts any
+		        given action and is not considering number of rounds, time or
+		        whether an action is allowed.
+		
+		@throws IllegalArgumentException if party doing action already acted.
+		                                 This is just a safety check as legality
+		                                 of actions should be checked before
+		                                 calling this.
+		'''
+		if not action.getActor() in self._notYetActed:
+			raise ValueError("actor already acted: " + str(action))
+		if isinstance(action, EndNegotiation):
+			return self.WithWalkAway(action.getActor())
+		newActions = list(self._actions)
+		newActions.append(action)
+		return PartyStates(self._powers, self._removeParty(action.getActor()), newActions,
+				self._agreements, self._walkedAway, self._exceptions)
+
+	def WithAgreements(self, newAgree:Agreements )->"PartyStates" :
+		'''
+		 @param newAgree a new agreements to be merged with existing agreements.
+		                 The parties in the agreement must have acted and thus in
+		                 actions.
+		 @return new PartyStates with agreeing parties removed from actions and
+		         added to Agreements.
+		'''
+		parties = newAgree.getMap().keys()
+		newActions = [act for act in self._actions if not act.getActor() in parties] 
+		return PartyStates(self._powers, self._notYetActed, newActions,
+				self._agreements.With(newAgree), self._walkedAway, self._exceptions)
+
+
+	def  WithWalkAway(self, actor:PartyId ) ->"PartyStates": 
+		if not actor in self._notYetActed:
+			raise ValueError("actor already acted: " + str(actor))
+		newWalkAway = list(self._walkedAway)
+		newWalkAway.append(actor)
+		return PartyStates(self._powers,self._removeParty(actor), self._actions, self._agreements,
+				newWalkAway, self._exceptions)
+
+	def  WithException(self, e:ProtocolException ) -> "PartyStates":
+		'''
+		Move party from active to exceptions.
+		
+		@param e the exception that the party caused
+		@return new PartyStates with party that caused the exception in the
+		        exceptions list and removed from the active list. Nothing happens
+		        if party is not active.
+	'''
+		if not e.getParty() in self._notYetActed:
+			# complex case. Party did valid action but now is messing around.
+			# Easiest seems to completely ignore it. CHECK.
+			return self;
+
+		newExc = dict(self._exceptions)
+		newExc[val(e.getParty())]= e
+		return PartyStates(self._powers, self._removeParty(val(e.getParty())), 
+			self._actions, self._agreements,self._walkedAway, newExc)
+
+	def  getNotYetActed(self)->Set[PartyId]:
+		'''
+		@return parties that have not yet acted
+
+		'''
+		return self._notYetActed
+
+	def  getNegotiatingParties(self)-> Set[PartyId]:
+		'''
+		@return parties that are still in the negotiation. These are the parties
+		        that not yet acted plus the ones that did an action.
+		'''
+		parties = set([ act.getActor() for act in self._actions ])
+		
+		parties = parties.union(self._notYetActed)
+		return parties
+
+	def _removeParty(self, party:PartyId )->Set[PartyId] :
+		'''
+		@param actor
+		@return {@link #notYetActed} with actor removed
+		 @throws IllegalArgumentException if party already acted (not in
+		                                 {@link #notYetActed}).
+	'''
+		newActiveParties = set(self._notYetActed)
+		if not party in newActiveParties:
+			raise ValueError(
+					"Party " + str(party) + " is not active, can't be removed")
+		newActiveParties.remove(party)
+		return newActiveParties
+
+	def getAgreements(self)->Agreements :
+		return self._agreements
+
+	def getPowers(self)->Dict[PartyId,int] :
+		return dict(self._powers)
+
+	def  getExceptions(self)->Dict[PartyId, ProtocolException]:
+		return dict(self._exceptions)
+
+	def finish(self)->"PartyStates": 
+		'''
+		@return new state where all {@link #notYetActed} are moved to the
+		        exceptions list.
+		'''
+		newstate = self
+		for party in self._notYetActed:
+			newstate = newstate.WithException(ProtocolException("Party did not act", party))
+		return newstate
+
+	def getActions(self)->List[Action] :
+		return list(self._actions)
+
+	def getWalkedAway(self)->List[PartyId] :
+		'''
+		@return all parties that walked away
+		'''
+		return list(self._walkedAway)
+
+# 	T=TypeVar("T", Action)
+	def  getActionsOfType(self, typ) -> List:
+		'''
+		@param <T>  the type of objects requested
+		@param typ the type of actions to extract. Must be of type T, needed
+			    because of java's type erasure.
+		
+		@return all actions of requested type in this phase
+		''' 
+		return [act for act in self._actions if isinstance(act, typ) ]
+
+	def __repr__(self):
+		return "PartyStates[" + str(list(self._notYetActed)) + "," + str(self._actions) + "," + \
+			str(self._agreements)+  "," + str(self._walkedAway) + "," + toStr(self._exceptions) + "]"
+
+	def flush(self)->"PartyStates" :
+		'''
+		@return states with all parties that are in {@link #actions} moved back
+		        to {@link #notYetActed}. This state is then ready for a next
+		        phase.
+		@throws IllegalStateException if {@link #notYetActed} is not empty
+		'''
+		if len(self._notYetActed)!=0:
+			raise ValueError(
+					"Some parties did not yet act:" + str(self._notYetActed))
+		return PartyStates(self._powers, self.getNegotiatingParties(), [],
+				self._agreements, self._walkedAway, self._exceptions)
+
+	def __hash__(self):
+		return hash((tuple(self._actions), self._agreements, toTuple(self._exceptions), 
+					tuple(self._notYetActed), toTuple(self._powers), tuple(self._walkedAway)))
+
+	def __eq__(self, other):
+		return isinstance(other, self.__class__)\
+			and self._actions == other._actions \
+			and self._agreements == other._agreements \
+			and self._exceptions == other._exceptions \
+			and self._notYetActed == other._notYetActed \
+			and self._powers == other._powers \
+			and self._walkedAway == other._walkedAway 
+
Index: geniuswebcore/geniusweb/protocol/session/mopac/phase/DefaultPhase.py
===================================================================
--- geniuswebcore/geniusweb/protocol/session/mopac/phase/DefaultPhase.py	(revision 69)
+++ geniuswebcore/geniusweb/protocol/session/mopac/phase/DefaultPhase.py	(revision 69)
@@ -0,0 +1,100 @@
+from abc import abstractmethod
+
+from geniusweb.actions.Action import Action
+from geniusweb.actions.PartyId import PartyId
+from geniusweb.protocol.ProtocolException import ProtocolException
+from geniusweb.protocol.session.mopac.PartyStates import PartyStates
+from geniusweb.protocol.session.mopac.phase.Phase import Phase, PHASE_MINTIME, \
+	PHASE_MAXTIME
+from geniusweb.voting.VotingEvaluator import VotingEvaluator
+
+
+class DefaultPhase(Phase):
+	# deadline for this phase, ms since 1970
+	
+	def __init__(self,  partyStates:PartyStates, deadline:int,
+			evaluator:VotingEvaluator ): 
+		'''
+		@param partyStates the {@link PartyStates}
+		@param deadline    deadline for this phase, ms since 1970
+		@param evaluator   the {@link VotingEvaluator} to be used
+		'''
+		self._partyStates = partyStates
+		self._deadline = deadline
+		self._evaluator = evaluator
+
+	def isFinal(self, now:int)->bool:
+		return now >= self._deadline or len(self._partyStates.getNotYetActed())==0
+
+	def getDeadline(self)->int:
+		return self._deadline
+
+	def _checkAction(self, actor:PartyId ,action: Action , now:int):
+		'''
+		Check if actor can do given action. Basic checks:
+		<ul>
+		<li>real actor does not match action's actor
+		<li>deadline for this phase has passed
+		<li>action is not allowed in this phase
+		<li>actor already acted in this phase
+		</ul>
+		@param actor  the actor that really acted
+		@param action the action that was done
+		@param now    current time
+		@throws ProtocolException if the action violates the protocol
+		'''
+		if action == None:
+			raise ProtocolException("Action=null", actor)
+		if actor != action.getActor():
+			raise ProtocolException(
+					"Incorrect actor info in action:" + str(action.getActor()),
+					actor)
+		if self.isFinal(now):
+			raise ProtocolException("passed deadline", actor)
+		if not type(action) in self.getAllowedActions():
+			raise ProtocolException("Action not allowed in "
+					+ type(self).__name__ + ": " + str(action), actor)
+		if not actor in self._partyStates.getNotYetActed():
+			raise ProtocolException("Actor can not act anymore", actor)
+		return
+
+	def getPartyStates(self)->PartyStates :
+		'''
+		@return current PartyStates
+		'''
+		return self._partyStates
+
+	def getEvaluator(self)->VotingEvaluator :
+		return self._evaluator
+
+	def next(self, now:int, duration:int)->Phase : 
+		if duration < PHASE_MINTIME or duration > PHASE_MAXTIME:
+			raise ValueError("Bug, illegal duration")
+		if not self.isFinal(now):
+			raise ValueError("phase is not final")
+		return self._checkedNext(now + duration)
+
+	@abstractmethod
+	def  _checkedNext(self, dl:int)-> Phase:
+		'''
+		As {@link #next(long, long)} but DefaultPhase already checked that 1.
+		there is enough time for a next phase 2. current state is final 3.
+		
+		@param dl the deadline for the next phase (ms since 1970).
+		@return the next phase
+		'''
+
+	def __repr__(self):
+		return type(self).__name__ + "[" + str(self._partyStates) + "," + \
+			str(self._deadline) + "," + str(self._evaluator) + "]"
+
+	def __hash__(self):
+		return hash((self._deadline, self._evaluator, self._partyStates))
+
+
+	def __eq__(self, other):
+		return isinstance(other, self.__class__) \
+			and self._partyStates==other._partyStates \
+			and self._deadline==other._deadline\
+			and self._evaluator==other._evaluator
+
Index: geniuswebcore/geniusweb/protocol/session/mopac/phase/OfferPhase.py
===================================================================
--- geniuswebcore/geniusweb/protocol/session/mopac/phase/OfferPhase.py	(revision 69)
+++ geniuswebcore/geniusweb/protocol/session/mopac/phase/OfferPhase.py	(revision 69)
@@ -0,0 +1,46 @@
+from typing import List, Any
+
+from geniusweb.actions.Action import Action
+from geniusweb.actions.EndNegotiation import EndNegotiation
+from geniusweb.actions.Offer import Offer
+from geniusweb.actions.PartyId import PartyId
+from geniusweb.inform.Inform import Inform
+from geniusweb.inform.YourTurn import YourTurn
+from geniusweb.protocol.ProtocolException import ProtocolException
+from geniusweb.protocol.session.mopac.phase.DefaultPhase import DefaultPhase
+from geniusweb.protocol.session.mopac.phase.Phase import Phase
+from geniusweb.protocol.session.mopac.phase.VotingPhase import VotingPhase
+
+
+class OfferPhase (DefaultPhase):
+
+
+	def With(self, actor:PartyId , action:Action ,timems:int)->"OfferPhase" :
+		try:
+			self._checkAction(actor, action, timems)
+		except ProtocolException as ex:
+			return self.WithException(ex)
+		return OfferPhase(self._partyStates.WithAction(action), self._deadline, self._evaluator);
+
+	def getInform(self)->Inform :
+		return YourTurn()
+
+	def WithException(self,  e:ProtocolException) -> "OfferPhase" :
+		# don't print to stdout
+		# System.out.println("Party kicked because of protocol exception:" + e);
+		return OfferPhase(self._partyStates.WithException(e), self._deadline, 
+						self._evaluator)
+
+	def finish(self)->Phase :
+		return OfferPhase(self._partyStates.finish(), self._deadline, self._evaluator)
+
+	def _checkedNext(self,  deadln:int) ->VotingPhase :
+		return VotingPhase(self._getOffers(), self._partyStates.flush(), deadln,
+				self._evaluator)
+
+	def _getOffers(self)->List[Offer] :
+		return self._partyStates.getActionsOfType(Offer)
+
+	def  getAllowedActions(self)->List[Any] : #Class<? extends Action>> 
+		return [Offer, EndNegotiation]
+
Index: geniuswebcore/geniusweb/protocol/session/mopac/phase/OptInPhase.py
===================================================================
--- geniuswebcore/geniusweb/protocol/session/mopac/phase/OptInPhase.py	(revision 69)
+++ geniuswebcore/geniusweb/protocol/session/mopac/phase/OptInPhase.py	(revision 69)
@@ -0,0 +1,83 @@
+from typing import List, Any
+
+from geniusweb.actions.Action import Action
+from geniusweb.actions.EndNegotiation import EndNegotiation
+from geniusweb.actions.PartyId import PartyId
+from geniusweb.actions.Votes import Votes
+from geniusweb.inform.Inform import Inform
+from geniusweb.inform.OptIn import OptIn
+from geniusweb.protocol.ProtocolException import ProtocolException
+from geniusweb.protocol.session.mopac.PartyStates import PartyStates
+from geniusweb.protocol.session.mopac.phase.DefaultPhase import DefaultPhase
+from geniusweb.protocol.session.mopac.phase.Phase import Phase
+from geniusweb.voting.CollectedVotes import CollectedVotes
+from geniusweb.voting.VotingEvaluator import VotingEvaluator
+
+
+class OptInPhase ( DefaultPhase ):
+
+	def __init__(self, votes:List[Votes] ,
+			partyStates:PartyStates ,
+			deadline:int,
+			evaluator:VotingEvaluator ):
+		super().__init__(partyStates, deadline, evaluator)
+		self._votes = votes
+		if votes==None:
+			raise ValueError("votes must be not null")
+
+	def getInform(self)->Inform :
+		return OptIn(self._votes)
+
+	def With(self, actor:PartyId , action:Action , now:int)->Phase :
+		try:
+			self._checkAction(actor, action, now)
+			if isinstance(action,Votes):
+				self._checkExtends(action)
+
+			return OptInPhase(self._votes, self._partyStates.WithAction(action), 
+							self._deadline, self._evaluator)
+
+		except ProtocolException as e:
+			return self.WithException(e)
+
+	def _checkExtends(self, newvotes:Votes ) :
+		'''
+		Check that this action extends previous action.
+		
+		@param newvotes new {@link Votes} just received
+		@throws ProtocolException if this action does not correctly extend
+		      previous vote.
+		'''
+		actor = newvotes.getActor();
+		# this actor is active so he must have voted in previous round.
+		prevVotes = [v for v in self._votes if v.getActor() == actor ][0]
+		if not newvotes.isExtending(prevVotes):
+			raise ProtocolException("New votes " + str(newvotes)
+					+ " does not extend previous vote " + str(prevVotes), actor)
+
+	def WithException(self,  e:ProtocolException) ->"OptInPhase" :
+		return OptInPhase(self._votes, self._partyStates.WithException(e), self._deadline, self._evaluator)
+
+	def finish(self)-> "OptInPhase":
+		states = self._partyStates.finish()
+		votesmap = { }
+		for v in states.getActionsOfType(Votes):
+			votesmap[v.getActor()]=v
+		allvotes = CollectedVotes(votesmap, states.getPowers())
+		newAgree = self._evaluator.create(allvotes).getAgreements()
+		if len(newAgree.getMap())>0:
+			print("detected new agreements")
+		finalStates = states.WithAgreements(newAgree)
+
+		return OptInPhase(self._votes, finalStates, self._deadline, self._evaluator);
+
+	def _checkedNext(self, newdeadline:int) ->"OfferPhase" : #type:ignore
+		from geniusweb.protocol.session.mopac.phase.OfferPhase import OfferPhase
+		return OfferPhase(self._partyStates.flush(), newdeadline, self._evaluator)
+
+	def getAllowedActions(self) -> List[Any]: #<Class<? extends Action>> :
+		return [Votes, EndNegotiation]
+
+	def getVotes(self)->List[Votes]:
+		return list(self._votes)
+
Index: geniuswebcore/geniusweb/protocol/session/mopac/phase/Phase.py
===================================================================
--- geniuswebcore/geniusweb/protocol/session/mopac/phase/Phase.py	(revision 69)
+++ geniuswebcore/geniusweb/protocol/session/mopac/phase/Phase.py	(revision 69)
@@ -0,0 +1,113 @@
+from abc import ABC
+from typing import List, Any
+
+from pyson.JsonSubTypes import JsonSubTypes
+from pyson.JsonTypeInfo import JsonTypeInfo, Id, As
+
+from geniusweb.actions.Action import Action
+from geniusweb.actions.PartyId import PartyId
+from geniusweb.inform.Inform import Inform
+from geniusweb.protocol.ProtocolException import ProtocolException
+from geniusweb.protocol.session.mopac.PartyStates import PartyStates 
+from geniusweb.voting.VotingEvaluator import VotingEvaluator
+
+
+PHASE_MAXTIME = 30000 # 30sec
+PHASE_MINTIME = 100   # 100 millisec
+
+
+@JsonTypeInfo(use=Id.NAME, include=As.WRAPPER_OBJECT)
+@JsonSubTypes(["geniusweb.protocol.session.mopac.phase.OfferPhase.OfferPhase",
+			"geniusweb.protocol.session.mopac.phase.OptInPhase.OptInPhase",
+			"geniusweb.protocol.session.mopac.phase.VotingPhase.VotingPhase"])
+class Phase(ABC):
+	'''
+	A Phase is a part of the round structure. In each round parties have to take
+	multiple actions. Each action is part of a phase.
+	
+	Invariant: A phase object handles negotiation events, ensuring that the
+	events are handled according to the possible actions in this phase. A phase
+	object must always remain in a consistent state. It does so by modifying the
+	contained {@link PartyStates} as needed.
+	<p>
+	The standard exception handling is assumed: unchecked exceptions are thrown
+	only if there is a bug in the protocol. Because none of the functions in this
+	interface throws, any error must be either ignored or the party must be
+	kicked.
+	'''
+
+	def With(self, actor:PartyId ,action: Action ,now:int)->"Phase" :
+		'''
+		Handle an actor's action. If a {@link ProtocolException} occurs, this is
+		handled by updating the PartyStates
+		
+		@param actor  the real actor (may differ from the actor contained in the
+		              action)
+		@param action the action submitted by actor, which this phase can really
+		              handle.
+		@param now    the current time
+		@return new VotingPhase
+		'''
+
+	def WithException(self,  e:ProtocolException)->"Phase" :
+		'''
+		@param e a {@link ProtocolException}
+		@return new Phase with a party disabled because it violated a protocol eg
+	          breaking a websocket link.
+		'''
+
+	def getInform(self)->Inform :
+		'''
+		@return the inform object to send to all parties at start of phase.
+		'''
+
+	def isFinal(self, now:int) -> bool:
+		'''
+		@param now the current time
+		@return true iff past deadline or no more actions are allowed anyway.
+		        (which usually means, all parties have done exactly one act).
+		        Notice, this is the main reason that it is desirable to require
+		        all parties to act exactly once in each phase. Without such a
+		        rule, the protocol will always have to wait till the deadline.
+		'''
+
+	def finish(self)->"Phase" :
+		'''
+		@return finished state. That means, all parties that did not act have
+		         been kicked, new agreements have been computed, etc.
+		'''
+
+	def next(self, now:int, duration:int) -> "Phase" :
+		'''
+		Determines the next phase. Resets the actions field. Can only be called
+		after {@link #finish()}
+		
+		@param now      the current time
+		@param duration the max duration (ms) of the next phase. Must be between
+		                {@link #PHASE_MINTIME} and {@link #PHASE_MAXTIME}. Also
+		                make sure that now+duration is at most at the total
+		                negotiation deadline.
+		@return the next phase, or null if negotiation is complete.
+		'''
+
+	def getEvaluator(self)->VotingEvaluator :
+		'''
+		@return the voting evaluator
+		'''
+
+	def getDeadline(self)->int:
+		'''
+		@return time (ms since 1970) at which phase ends. The phase may end
+    	    before, but never after this.
+		'''
+
+	def getPartyStates(self)->PartyStates :
+		'''
+		@return the party states. Notice that someone must call
+	        {@link PartyStates#finish()} if passed the deadline.
+		'''
+
+	def  getAllowedActions(self) -> List[Any]: # Class<? extends Action>... 
+		'''
+		@return the allowed actinos in this phase
+		'''
Index: geniuswebcore/geniusweb/protocol/session/mopac/phase/VotingPhase.py
===================================================================
--- geniuswebcore/geniusweb/protocol/session/mopac/phase/VotingPhase.py	(revision 69)
+++ geniuswebcore/geniusweb/protocol/session/mopac/phase/VotingPhase.py	(revision 69)
@@ -0,0 +1,65 @@
+from typing import List, Any
+
+from geniusweb.actions.Action import Action
+from geniusweb.actions.EndNegotiation import EndNegotiation
+from geniusweb.actions.Offer import Offer
+from geniusweb.actions.PartyId import PartyId
+from geniusweb.actions.Votes import Votes
+from geniusweb.inform.Inform import Inform
+from geniusweb.inform.Voting import Voting
+from geniusweb.protocol.ProtocolException import ProtocolException
+from geniusweb.protocol.session.mopac.PartyStates import PartyStates
+from geniusweb.protocol.session.mopac.phase.DefaultPhase import DefaultPhase
+from geniusweb.protocol.session.mopac.phase.OptInPhase import OptInPhase
+from geniusweb.protocol.session.mopac.phase.Phase import Phase
+from geniusweb.voting.VotingEvaluator import VotingEvaluator
+
+
+class VotingPhase (DefaultPhase):
+
+	def __init__(self,  offers: List[Offer],
+			partyStates:PartyStates,
+			deadline: int,
+			evaluator:VotingEvaluator ) :
+		super().__init__(partyStates, deadline, evaluator)
+		self._offers = offers
+		if offers==None:
+			raise ValueError("offers must be not null")
+
+	def With(self, actor:PartyId ,action: Action , now:int)-> "VotingPhase" :
+		try:
+			self._checkAction(actor, action, now)
+		except ProtocolException as ex:
+			return self.WithException(ex)
+		return VotingPhase(self._offers, self._partyStates.WithAction(action), self._deadline,
+				self._evaluator);
+
+	def getInform(self)->Inform :
+		return Voting(self._offers, self._partyStates.getPowers())
+
+	def WithException(self, e:ProtocolException ) ->"VotingPhase" :
+		return VotingPhase(self._offers, self._partyStates.WithException(e), self._deadline,
+				self._evaluator)
+
+	def finish(self)->"VotingPhase" :
+		return VotingPhase(self._offers, self._partyStates.finish(), self._deadline,
+				self._evaluator)
+
+	def _checkedNext(self,deadln:int) ->Phase :
+		return OptInPhase(self.getVotes(), self._partyStates.flush(), deadln,
+				self._evaluator);
+
+	def getAllowedActions(self) ->List[Any]: #Class<? extends Action>>: 
+		return [Votes, EndNegotiation]
+
+	def  getVotes(self)->List[Votes]:
+		'''
+		@return all votes done in this phase.
+		'''
+		return  [ act for act in self._partyStates.getActions() if isinstance(act, Votes)]
+	
+	def getOffers(self)->List[Offer]:
+		'''
+		@return the offers received in the {@link OfferPhase}
+		'''
+		return list(self._offers)
