Game updates improvements
* Added a set of tables which define game updates and their targets. These definitions replace the old enumerate type. Added a set of triggers which automatically create specific update tables, insert missing entries, etc... when game update types are being manipulated. * Removed manual insertion of game updates from empire creation function and universe generator. * Added registration of core update targets (i.e. planets and empires), updated all existing game update processing functions and added type registrations * Created Maven project for game updates control components, moved existing components from the -simple project, rewritten most of what they contained, added new components for server-side update batch processing
This commit is contained in:
parent
ba6a1e2b41
commit
56eddcc4f0
93 changed files with 4004 additions and 578 deletions
|
@ -1,138 +0,0 @@
|
||||||
package com.deepclone.lw.beans.updates;
|
|
||||||
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
|
||||||
import org.springframework.transaction.TransactionStatus;
|
|
||||||
import org.springframework.transaction.support.TransactionCallback;
|
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
|
||||||
|
|
||||||
import com.deepclone.lw.cmd.admin.logs.LogLevel;
|
|
||||||
import com.deepclone.lw.interfaces.eventlog.Logger;
|
|
||||||
import com.deepclone.lw.interfaces.eventlog.SystemLogger;
|
|
||||||
import com.deepclone.lw.interfaces.game.UpdatesDAO;
|
|
||||||
import com.deepclone.lw.interfaces.sys.MaintenanceStatusException;
|
|
||||||
import com.deepclone.lw.interfaces.sys.SystemStatus;
|
|
||||||
import com.deepclone.lw.interfaces.sys.TickStatusException;
|
|
||||||
import com.deepclone.lw.interfaces.sys.Ticker;
|
|
||||||
import com.deepclone.lw.interfaces.sys.Ticker.Frequency;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class GameUpdateBean
|
|
||||||
implements InitializingBean , Runnable
|
|
||||||
{
|
|
||||||
private Ticker ticker;
|
|
||||||
private SystemStatus systemStatus;
|
|
||||||
private SystemLogger logger;
|
|
||||||
|
|
||||||
private TransactionTemplate tTemplate;
|
|
||||||
private UpdatesDAO updatesDao;
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired( required = true )
|
|
||||||
public void setTicker( Ticker ticker )
|
|
||||||
{
|
|
||||||
this.ticker = ticker;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired( required = true )
|
|
||||||
public void setSystemStatus( SystemStatus systemStatus )
|
|
||||||
{
|
|
||||||
this.systemStatus = systemStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired( required = true )
|
|
||||||
public void setLogger( Logger logger )
|
|
||||||
{
|
|
||||||
this.logger = logger.getSystemLogger( "GameUpdate" );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired( required = true )
|
|
||||||
public void setTransactionManager( PlatformTransactionManager transactionManager )
|
|
||||||
{
|
|
||||||
this.tTemplate = new TransactionTemplate( transactionManager );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired( required = true )
|
|
||||||
public void setUpdatesDAO( UpdatesDAO updatesDao )
|
|
||||||
{
|
|
||||||
this.updatesDao = updatesDao;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterPropertiesSet( )
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
this.endPreviousTick( );
|
|
||||||
} catch ( MaintenanceStatusException e ) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
this.ticker.registerTask( Frequency.MINUTE , "Game update" , this );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run( )
|
|
||||||
{
|
|
||||||
// Attempt to end the previous tick, if e.g. maintenance mode was initiated while it was
|
|
||||||
// being processed
|
|
||||||
try {
|
|
||||||
this.endPreviousTick( );
|
|
||||||
} catch ( MaintenanceStatusException e1 ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initiate next tick
|
|
||||||
long tickId;
|
|
||||||
try {
|
|
||||||
tickId = this.systemStatus.startTick( );
|
|
||||||
} catch ( TickStatusException e ) {
|
|
||||||
throw new RuntimeException( "tick initiated while previous tick still being processed" , e );
|
|
||||||
} catch ( MaintenanceStatusException e ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute tick
|
|
||||||
this.logger.log( LogLevel.DEBUG , "Tick " + tickId + " started" ).flush( );
|
|
||||||
this.executeTick( tickId );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void endPreviousTick( )
|
|
||||||
throws MaintenanceStatusException
|
|
||||||
{
|
|
||||||
Long currentTick = this.systemStatus.checkStuckTick( );
|
|
||||||
if ( currentTick == null ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.log( LogLevel.WARNING , "Tick " + currentTick + " restarted" ).flush( );
|
|
||||||
this.executeTick( currentTick.longValue( ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void executeTick( final long tickId )
|
|
||||||
{
|
|
||||||
boolean hasMore;
|
|
||||||
do {
|
|
||||||
hasMore = this.tTemplate.execute( new TransactionCallback< Boolean >( ) {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Boolean doInTransaction( TransactionStatus status )
|
|
||||||
{
|
|
||||||
return updatesDao.processUpdates( tickId );
|
|
||||||
}
|
|
||||||
|
|
||||||
} );
|
|
||||||
} while ( hasMore );
|
|
||||||
this.logger.log( LogLevel.TRACE , "Tick " + tickId + " completed" ).flush( );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
package com.deepclone.lw.beans.updates;
|
|
||||||
|
|
||||||
|
|
||||||
import java.sql.Types;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.jdbc.core.SqlOutParameter;
|
|
||||||
import org.springframework.jdbc.core.SqlParameter;
|
|
||||||
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
|
|
||||||
|
|
||||||
import com.deepclone.lw.interfaces.game.UpdatesDAO;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public class UpdatesDAOBean
|
|
||||||
implements UpdatesDAO
|
|
||||||
{
|
|
||||||
|
|
||||||
private SimpleJdbcCall process;
|
|
||||||
|
|
||||||
|
|
||||||
@Autowired( required = true )
|
|
||||||
public void setDataSource( DataSource dataSource )
|
|
||||||
{
|
|
||||||
this.process = new SimpleJdbcCall( dataSource );
|
|
||||||
this.process.withCatalogName( "sys" ).withFunctionName( "process_updates" );
|
|
||||||
this.process.withoutProcedureColumnMetaDataAccess( );
|
|
||||||
this.process.addDeclaredParameter( new SqlParameter( "tick_id" , Types.BIGINT ) );
|
|
||||||
this.process.addDeclaredParameter( new SqlOutParameter( "has_more" , Types.BOOLEAN ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean processUpdates( long tickId )
|
|
||||||
{
|
|
||||||
Map< String , Object > m = this.process.execute( tickId );
|
|
||||||
return (Boolean) m.get( "has_more" );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -10,13 +10,11 @@
|
||||||
<import resource="simple/empire-management-bean.xml" />
|
<import resource="simple/empire-management-bean.xml" />
|
||||||
<import resource="simple/fleet-management-bean.xml" />
|
<import resource="simple/fleet-management-bean.xml" />
|
||||||
<import resource="simple/fleets-dao-bean.xml" />
|
<import resource="simple/fleets-dao-bean.xml" />
|
||||||
<import resource="simple/game-update-bean.xml" />
|
|
||||||
<import resource="simple/map-viewer-bean.xml" />
|
<import resource="simple/map-viewer-bean.xml" />
|
||||||
<import resource="simple/message-beans.xml" />
|
<import resource="simple/message-beans.xml" />
|
||||||
<import resource="simple/planet-dao-bean.xml" />
|
<import resource="simple/planet-dao-bean.xml" />
|
||||||
<import resource="simple/planets-management-bean.xml" />
|
<import resource="simple/planets-management-bean.xml" />
|
||||||
<import resource="simple/universe-dao-bean.xml" />
|
<import resource="simple/universe-dao-bean.xml" />
|
||||||
<import resource="simple/universe-generator-bean.xml" />
|
<import resource="simple/universe-generator-bean.xml" />
|
||||||
<import resource="simple/updates-dao-bean.xml" />
|
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="
|
|
||||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
|
||||||
|
|
||||||
<!-- Game update bean -->
|
|
||||||
<bean id="gameUpdate" class="com.deepclone.lw.beans.updates.GameUpdateBean" />
|
|
||||||
|
|
||||||
</beans>
|
|
17
legacyworlds-server-beans-updates/pom.xml
Normal file
17
legacyworlds-server-beans-updates/pom.xml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<artifactId>legacyworlds-server-beans</artifactId>
|
||||||
|
<groupId>com.deepclone.lw</groupId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<relativePath>../legacyworlds-server-beans/pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>legacyworlds-server-beans-updates</artifactId>
|
||||||
|
<name>Legacy Worlds - Server - Components - Game updates</name>
|
||||||
|
<version>${legacyworlds.version.main}.${legacyworlds.version.release}-${legacyworlds.version.build}</version>
|
||||||
|
<description>This module contains the components which process the game updates and allow the administration interface to display information about them.</description>
|
||||||
|
|
||||||
|
</project>
|
0
legacyworlds-server-beans-updates/src/main/java/.empty
Normal file
0
legacyworlds-server-beans-updates/src/main/java/.empty
Normal file
|
@ -0,0 +1,238 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import com.deepclone.lw.cmd.admin.logs.LogLevel;
|
||||||
|
import com.deepclone.lw.interfaces.eventlog.Logger;
|
||||||
|
import com.deepclone.lw.interfaces.eventlog.SystemLogger;
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.GameUpdateProcessor;
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.UpdatesDAO;
|
||||||
|
import com.deepclone.lw.interfaces.sys.MaintenanceStatusException;
|
||||||
|
import com.deepclone.lw.interfaces.sys.SystemStatus;
|
||||||
|
import com.deepclone.lw.interfaces.sys.TickStatusException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game update processor component
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This component implements game updates processing. It will update the system status as necessary,
|
||||||
|
* checking for maintenance mode, and of course implement the necessary operations for game updates.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
class GameUpdateProcessorBean
|
||||||
|
implements GameUpdateProcessor
|
||||||
|
{
|
||||||
|
/** System state management component */
|
||||||
|
private SystemStatus systemStatus;
|
||||||
|
|
||||||
|
/** Logger used by this component */
|
||||||
|
private SystemLogger logger;
|
||||||
|
|
||||||
|
/** Transaction template used to run update batches */
|
||||||
|
private TransactionTemplate tTemplate;
|
||||||
|
|
||||||
|
/** Update processing database interface */
|
||||||
|
private UpdatesDAO updatesDao;
|
||||||
|
|
||||||
|
/** Server-level update processors registry */
|
||||||
|
private ServerProcessorRegistry registry;
|
||||||
|
|
||||||
|
/** Game update processor lock */
|
||||||
|
private boolean executing;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency injector that sets the sytstem state management component
|
||||||
|
*
|
||||||
|
* @param systemStatus
|
||||||
|
* the sytstem state management component
|
||||||
|
*/
|
||||||
|
@Autowired( required = true )
|
||||||
|
public void setSystemStatus( SystemStatus systemStatus )
|
||||||
|
{
|
||||||
|
this.systemStatus = systemStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency injector that initialises the logger from the logging component
|
||||||
|
*
|
||||||
|
* @param logger
|
||||||
|
* the logging component
|
||||||
|
*/
|
||||||
|
@Autowired( required = true )
|
||||||
|
public void setLogger( Logger logger )
|
||||||
|
{
|
||||||
|
this.logger = logger.getSystemLogger( "GameUpdate" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency injector that initialises the transaction template from the transaction manager
|
||||||
|
*
|
||||||
|
* @param transactionManager
|
||||||
|
* the transaction manager
|
||||||
|
*/
|
||||||
|
@Autowired( required = true )
|
||||||
|
public void setTransactionManager( PlatformTransactionManager transactionManager )
|
||||||
|
{
|
||||||
|
this.tTemplate = new TransactionTemplate( transactionManager );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency injector that sets the database access component for game updates
|
||||||
|
*
|
||||||
|
* @param updatesDao
|
||||||
|
* the database access component for game updates
|
||||||
|
*/
|
||||||
|
@Autowired( required = true )
|
||||||
|
public void setUpdatesDAO( UpdatesDAO updatesDao )
|
||||||
|
{
|
||||||
|
this.updatesDao = updatesDao;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency injector that sets the processor registry
|
||||||
|
*
|
||||||
|
* @param registry
|
||||||
|
* the processor registry
|
||||||
|
*/
|
||||||
|
@Autowired( required = true )
|
||||||
|
public void setRegistry( ServerProcessorRegistry registry )
|
||||||
|
{
|
||||||
|
this.registry = registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try and lock the processor
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Test the {@link #executing} field, setting it to <code>true</code> and returning
|
||||||
|
* <code>true</code> if it wasn't or returning <code>false</code> if it was.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public synchronized boolean tryLock( )
|
||||||
|
{
|
||||||
|
if ( this.executing ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.executing = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlock the processor
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Set {@link #executing} back to <code>false</code>. If it was already set to
|
||||||
|
* <code>false</code>, throw an {@link IllegalStateException}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public synchronized void unlock( )
|
||||||
|
throws IllegalStateException
|
||||||
|
{
|
||||||
|
if ( !this.executing ) {
|
||||||
|
throw new IllegalStateException( "Trying to unlock game update processor, but it isn't locked" );
|
||||||
|
}
|
||||||
|
this.executing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for an unfinished update cycle and terminate it
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Check for a "stuck" update cycle. If there is one, issue a warning and process it through the
|
||||||
|
* {@link #executeTick(long)} method.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean endPreviousCycle( )
|
||||||
|
{
|
||||||
|
Long currentTick;
|
||||||
|
try {
|
||||||
|
currentTick = this.systemStatus.checkStuckTick( );
|
||||||
|
} catch ( MaintenanceStatusException e ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( currentTick == null ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log( LogLevel.WARNING , "Tick " + currentTick + " restarted" ).flush( );
|
||||||
|
this.executeTick( currentTick );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute an update cycle
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Start by checking for, and executing if necessary, a "stuck" update cycle. If one is found,
|
||||||
|
* return. Otherwise, start a new tick through the system state manager, and call
|
||||||
|
* {@link #executeTick(long)} to process it.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If maintenance mode is active, the new tick will not be started.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void executeUpdateCycle( )
|
||||||
|
{
|
||||||
|
// Attempt to end the previous tick, if e.g. maintenance mode was initiated while it was
|
||||||
|
// being processed
|
||||||
|
if ( this.endPreviousCycle( ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate next tick
|
||||||
|
long tickId;
|
||||||
|
try {
|
||||||
|
tickId = this.systemStatus.startTick( );
|
||||||
|
} catch ( TickStatusException e ) {
|
||||||
|
throw new RuntimeException( "tick initiated while previous tick still being processed" , e );
|
||||||
|
} catch ( MaintenanceStatusException e ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute tick
|
||||||
|
this.logger.log( LogLevel.DEBUG , "Tick " + tickId + " started" ).flush( );
|
||||||
|
this.executeTick( tickId );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process all remaining update batches for an update cycle
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method will try and process all update batches using a {@link GameUpdateTransaction}
|
||||||
|
* instance. It loops until the transaction indicates that no further updates are to be
|
||||||
|
* processed. However, the loop will exit if maintenance mode becomes active between update
|
||||||
|
* batches.
|
||||||
|
*
|
||||||
|
* @param tickId
|
||||||
|
* the game update cycle's identifier
|
||||||
|
*/
|
||||||
|
private void executeTick( final long tickId )
|
||||||
|
{
|
||||||
|
GameUpdateTransaction transaction = new GameUpdateTransaction( this.updatesDao , this.registry , tickId );
|
||||||
|
while ( !this.tTemplate.execute( transaction ) ) {
|
||||||
|
if ( this.systemStatus.checkMaintenance( ) != null ) {
|
||||||
|
this.logger.log( LogLevel.INFO , "Tick " + tickId + " interrupted by system maintenance" ).flush( );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.logger.log( LogLevel.TRACE , "Tick " + tickId + " completed" ).flush( );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.GameUpdateProcessor;
|
||||||
|
import com.deepclone.lw.interfaces.sys.Ticker;
|
||||||
|
import com.deepclone.lw.interfaces.sys.Ticker.Frequency;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game update ticker task
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This component will register itself as a ticker task to be executed once per minute. When it
|
||||||
|
* runs, it will try and execute an update cycle.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
class GameUpdateTaskBean
|
||||||
|
implements InitializingBean , Runnable
|
||||||
|
{
|
||||||
|
/** The ticker component */
|
||||||
|
private Ticker ticker;
|
||||||
|
|
||||||
|
/** The game updates processor */
|
||||||
|
private GameUpdateProcessor gameUpdateProcessor;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency injector that sets the ticker component
|
||||||
|
*
|
||||||
|
* @param ticker
|
||||||
|
* the ticker component
|
||||||
|
*/
|
||||||
|
@Autowired( required = true )
|
||||||
|
public void setTicker( Ticker ticker )
|
||||||
|
{
|
||||||
|
this.ticker = ticker;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency injector that sets the game update processor
|
||||||
|
*
|
||||||
|
* @param gameUpdateProcessor
|
||||||
|
* the game update processor
|
||||||
|
*/
|
||||||
|
@Autowired( required = true )
|
||||||
|
public void setGameUpdateProcessor( GameUpdateProcessor gameUpdateProcessor )
|
||||||
|
{
|
||||||
|
this.gameUpdateProcessor = gameUpdateProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finish a previous update cycle if there is one, then register the component to the ticker
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet( )
|
||||||
|
{
|
||||||
|
if ( this.gameUpdateProcessor.tryLock( ) ) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.gameUpdateProcessor.endPreviousCycle( );
|
||||||
|
} finally {
|
||||||
|
this.gameUpdateProcessor.unlock( );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
this.ticker.registerTask( Frequency.MINUTE , "Game update" , this );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a game update cycle
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Try locking the game update processor. On success, execute an update cycle, then unlock the
|
||||||
|
* processor.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void run( )
|
||||||
|
{
|
||||||
|
if ( !this.gameUpdateProcessor.tryLock( ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.gameUpdateProcessor.executeUpdateCycle( );
|
||||||
|
} finally {
|
||||||
|
this.gameUpdateProcessor.unlock( );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.transaction.TransactionStatus;
|
||||||
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.UpdateBatchProcessor;
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.UpdatesDAO;
|
||||||
|
import com.deepclone.lw.sqld.sys.GameUpdateResult;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game update transaction
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class is responsible for executing the necessary operations during a game update
|
||||||
|
* transaction. It is used by the {@link GameUpdateProcessorBean} component to implement its main
|
||||||
|
* processing loop.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
class GameUpdateTransaction
|
||||||
|
implements TransactionCallback< Boolean >
|
||||||
|
{
|
||||||
|
/** The game updates database access interface */
|
||||||
|
private final UpdatesDAO updatesDao;
|
||||||
|
|
||||||
|
/** The registry of server-side update batch processors */
|
||||||
|
private final ServerProcessorRegistry registry;
|
||||||
|
|
||||||
|
/** The identifier of the current update cycle */
|
||||||
|
private final long tickId;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the various dependencies and set the identifier of the current cycle
|
||||||
|
*
|
||||||
|
* @param updatesDao
|
||||||
|
* the game updates database access interface
|
||||||
|
* @param registry
|
||||||
|
* the registry of server-side update batch processors
|
||||||
|
* @param tickId
|
||||||
|
* the identifier of the current update cycle
|
||||||
|
*/
|
||||||
|
GameUpdateTransaction( UpdatesDAO updatesDao , ServerProcessorRegistry registry , long tickId )
|
||||||
|
{
|
||||||
|
this.updatesDao = updatesDao;
|
||||||
|
this.registry = registry;
|
||||||
|
this.tickId = tickId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update batch processing transaction
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Call the database's game update processing function, returning immediately if the function
|
||||||
|
* indicates that the cycle has been completed. Otherwise, execute a server-side processor if
|
||||||
|
* necessary.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the database requests server-side processing but no processor is found, an
|
||||||
|
* {@link UnsupportedUpdateException} will be thrown.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if the current update cycle has been completed, <code>false</code>
|
||||||
|
* if more updates are needed.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Boolean doInTransaction( TransactionStatus status )
|
||||||
|
{
|
||||||
|
GameUpdateResult result = this.updatesDao.processUpdates( this.tickId );
|
||||||
|
String local = result.getLocalExecution( );
|
||||||
|
if ( result.isFinished( ) ) {
|
||||||
|
return true;
|
||||||
|
} else if ( local == null ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateBatchProcessor processor = this.registry.getProcessorFor( local );
|
||||||
|
if ( processor == null ) {
|
||||||
|
throw new UnsupportedUpdateException( local );
|
||||||
|
}
|
||||||
|
|
||||||
|
processor.processBatch( this.tickId );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.UpdateBatchProcessor;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server-side update processor registry
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This interface allows server-side game update batch processors to be looked up depending on the
|
||||||
|
* type of the updates to process.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
interface ServerProcessorRegistry
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a batch processor for some type of update
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* the type of updates to process, as stored in the <code>sys.update_types</code>
|
||||||
|
* database table
|
||||||
|
*
|
||||||
|
* @return <code>null</code> if no processor for the specified type has been registered, or the
|
||||||
|
* processor to use.
|
||||||
|
*/
|
||||||
|
public UpdateBatchProcessor getProcessorFor( String type );
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.BeanInitializationException;
|
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.UpdateBatchProcessor;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server-side update processor registry component
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This component will look through registered Spring components for update processors, and use the
|
||||||
|
* results as the registry's contents.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
class ServerProcessorRegistryBean
|
||||||
|
implements ServerProcessorRegistry , BeanPostProcessor
|
||||||
|
{
|
||||||
|
|
||||||
|
/** Known server-side update batch processors */
|
||||||
|
private final Map< String , UpdateBatchProcessor > processors = new HashMap< String , UpdateBatchProcessor >( );
|
||||||
|
|
||||||
|
|
||||||
|
/** Do nothing - we're not interested in uninitialised components */
|
||||||
|
@Override
|
||||||
|
public Object postProcessBeforeInitialization( Object bean , String beanName )
|
||||||
|
{
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If an initialised component implements the {@link UpdateBatchProcessor} interface, add it to
|
||||||
|
* the {@link #processors} map. A {@link BeanInitializationException} will be thrown if two
|
||||||
|
* components seem to process the same update type.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object postProcessAfterInitialization( Object bean , String beanName )
|
||||||
|
throws BeansException
|
||||||
|
{
|
||||||
|
if ( bean instanceof UpdateBatchProcessor ) {
|
||||||
|
UpdateBatchProcessor processor = (UpdateBatchProcessor) bean;
|
||||||
|
String updType = processor.getUpdateType( );
|
||||||
|
if ( this.processors.containsKey( updType ) ) {
|
||||||
|
throw new BeanInitializationException( "Duplicate update processor for update type " + updType );
|
||||||
|
}
|
||||||
|
this.processors.put( updType , processor );
|
||||||
|
}
|
||||||
|
return bean;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Obtain the processor from the {@link #processors} map */
|
||||||
|
@Override
|
||||||
|
public UpdateBatchProcessor getProcessorFor( String type )
|
||||||
|
{
|
||||||
|
return this.processors.get( type );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsupported update exception
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This exception is thrown by the {@link GameUpdateTransaction} when there is no server-side
|
||||||
|
* processor for some update type.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings( "serial" )
|
||||||
|
public class UnsupportedUpdateException
|
||||||
|
extends RuntimeException
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the exception
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* the name of the update type for which no processor is registered.
|
||||||
|
*/
|
||||||
|
UnsupportedUpdateException( String name )
|
||||||
|
{
|
||||||
|
super( "No processor found for update type " + name );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.UpdatesDAO;
|
||||||
|
import com.deepclone.lw.sqld.sys.GameUpdateResult;
|
||||||
|
import com.deepclone.lw.utils.StoredProc;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates data access component
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This component accesses the database procedures and views which constitute the game update
|
||||||
|
* system.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
class UpdatesDAOBean
|
||||||
|
implements UpdatesDAO
|
||||||
|
{
|
||||||
|
|
||||||
|
/** Game update processor stored procedure */
|
||||||
|
private StoredProc process;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dependency injector for the data source
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* When Spring injects the data source component, initialise the stored procedures used by the
|
||||||
|
* component.
|
||||||
|
*
|
||||||
|
* @param dataSource
|
||||||
|
* the data source component
|
||||||
|
*/
|
||||||
|
@Autowired( required = true )
|
||||||
|
public void setDataSource( DataSource dataSource )
|
||||||
|
{
|
||||||
|
this.process = new StoredProc( dataSource , "sys" , "process_updates" ).addParameter( "tick_id" , Types.BIGINT )
|
||||||
|
.addOutput( "_has_more" , Types.BOOLEAN ).addOutput( "_process_externally" , Types.VARCHAR );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the processor's stored procedure
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This implementation simply runs the database's <code>sys.process_updates()</code> stored
|
||||||
|
* procedure, and converts the two values it returns into a {@link GameUpdateResult} instance.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public GameUpdateResult processUpdates( long tickId )
|
||||||
|
{
|
||||||
|
Map< String , Object > m = this.process.execute( tickId );
|
||||||
|
if ( !(Boolean) m.get( "_has_more" ) ) {
|
||||||
|
return new GameUpdateResult( );
|
||||||
|
}
|
||||||
|
return new GameUpdateResult( (String) m.get( "_process_externally" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,8 +2,8 @@
|
||||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="
|
xsi:schemaLocation="
|
||||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
|
||||||
|
|
||||||
<bean id="updatesDAO" class="com.deepclone.lw.beans.updates.UpdatesDAOBean" />
|
<import resource="game/updates.xml" />
|
||||||
|
|
||||||
</beans>
|
</beans>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="
|
||||||
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||||
|
|
||||||
|
<bean id="gameUpdateDAO" class="com.deepclone.lw.beans.updates.UpdatesDAOBean" />
|
||||||
|
<bean id="gameUpdateProcessor" class="com.deepclone.lw.beans.updates.GameUpdateProcessorBean" />
|
||||||
|
<bean id="gameUpdateRegistry"
|
||||||
|
class="com.deepclone.lw.beans.updates.ServerProcessorRegistryBean" />
|
||||||
|
<bean id="gameUpdateTask" class="com.deepclone.lw.beans.updates.GameUpdateTaskBean" />
|
||||||
|
|
||||||
|
</beans>
|
0
legacyworlds-server-beans-updates/src/test/java/.empty
Normal file
0
legacyworlds-server-beans-updates/src/test/java/.empty
Normal file
|
@ -21,15 +21,16 @@
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<modules>
|
<modules>
|
||||||
<module>../legacyworlds-server-beans-i18n</module>
|
|
||||||
<module>../legacyworlds-server-beans-eventlog</module>
|
|
||||||
<module>../legacyworlds-server-beans-accounts</module>
|
<module>../legacyworlds-server-beans-accounts</module>
|
||||||
|
<module>../legacyworlds-server-beans-bt</module>
|
||||||
|
<module>../legacyworlds-server-beans-eventlog</module>
|
||||||
|
<module>../legacyworlds-server-beans-i18n</module>
|
||||||
<module>../legacyworlds-server-beans-mailer</module>
|
<module>../legacyworlds-server-beans-mailer</module>
|
||||||
<module>../legacyworlds-server-beans-system</module>
|
|
||||||
<module>../legacyworlds-server-beans-naming</module>
|
<module>../legacyworlds-server-beans-naming</module>
|
||||||
<module>../legacyworlds-server-beans-resources</module>
|
<module>../legacyworlds-server-beans-resources</module>
|
||||||
<module>../legacyworlds-server-beans-bt</module>
|
|
||||||
<module>../legacyworlds-server-beans-user</module>
|
|
||||||
<module>../legacyworlds-server-beans-simple</module>
|
<module>../legacyworlds-server-beans-simple</module>
|
||||||
|
<module>../legacyworlds-server-beans-system</module>
|
||||||
|
<module>../legacyworlds-server-beans-updates</module>
|
||||||
|
<module>../legacyworlds-server-beans-user</module>
|
||||||
</modules>
|
</modules>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
-- LegacyWorlds Beta 6
|
-- LegacyWorlds Beta 6
|
||||||
-- PostgreSQL database scripts
|
-- PostgreSQL database scripts
|
||||||
--
|
--
|
||||||
-- System & game updates status
|
-- System & ticker status
|
||||||
--
|
--
|
||||||
-- Copyright(C) 2004-2010, DeepClone Development
|
-- Copyright(C) 2004-2010, DeepClone Development
|
||||||
-- --------------------------------------------------------
|
-- --------------------------------------------------------
|
||||||
|
@ -53,55 +53,3 @@ INSERT INTO sys.ticker( task_name , status )
|
||||||
VALUES ( 'Game update' , 'STOPPED' );
|
VALUES ( 'Game update' , 'STOPPED' );
|
||||||
|
|
||||||
GRANT SELECT ON sys.ticker TO :dbuser;
|
GRANT SELECT ON sys.ticker TO :dbuser;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Updates
|
|
||||||
--
|
|
||||||
CREATE TABLE sys.updates(
|
|
||||||
id BIGSERIAL NOT NULL PRIMARY KEY ,
|
|
||||||
gu_type update_type NOT NULL ,
|
|
||||||
status processing_status NOT NULL DEFAULT 'FUTURE' ,
|
|
||||||
last_tick BIGINT NOT NULL DEFAULT -1
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_updates_finder
|
|
||||||
ON sys.updates (gu_type, status, last_tick);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Planet updates
|
|
||||||
--
|
|
||||||
CREATE TABLE verse.updates(
|
|
||||||
update_id BIGINT NOT NULL PRIMARY KEY ,
|
|
||||||
planet_id INT NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_planetupdates_planet
|
|
||||||
ON verse.updates (planet_id);
|
|
||||||
|
|
||||||
ALTER TABLE verse.updates
|
|
||||||
ADD CONSTRAINT fk_planetupdates_update
|
|
||||||
FOREIGN KEY ( update_id ) REFERENCES sys.updates ,
|
|
||||||
ADD CONSTRAINT fk_planetupdates_planet
|
|
||||||
FOREIGN KEY ( planet_id ) REFERENCES verse.planets;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Empire updates
|
|
||||||
--
|
|
||||||
CREATE TABLE emp.updates(
|
|
||||||
update_id BIGINT NOT NULL PRIMARY KEY ,
|
|
||||||
empire_id INT NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE INDEX idx_empireupdates_empire
|
|
||||||
ON emp.updates( empire_id );
|
|
||||||
|
|
||||||
ALTER TABLE emp.updates
|
|
||||||
ADD CONSTRAINT fk_empireupdates_update
|
|
||||||
FOREIGN KEY ( update_id ) REFERENCES sys.updates ,
|
|
||||||
ADD CONSTRAINT fk_empireupdates_empire
|
|
||||||
FOREIGN KEY ( empire_id ) REFERENCES emp.empires
|
|
||||||
ON DELETE CASCADE;
|
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
-- LegacyWorlds Beta 6
|
||||||
|
-- PostgreSQL database scripts
|
||||||
|
--
|
||||||
|
-- Game updates control tables
|
||||||
|
--
|
||||||
|
-- Copyright(C) 2004-2012, DeepClone Development
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update targets
|
||||||
|
* ---------------
|
||||||
|
*
|
||||||
|
* An update target refers to a table whose rows are the units to be updated
|
||||||
|
* by some step of the game update.
|
||||||
|
*
|
||||||
|
* Inserting rows into this control table will generate a new, specific table
|
||||||
|
* to store references, and a set of triggers.
|
||||||
|
*/
|
||||||
|
DROP TABLE IF EXISTS sys.update_targets CASCADE;
|
||||||
|
CREATE TABLE sys.update_targets(
|
||||||
|
/* Automatically generated identifier of the target type */
|
||||||
|
updtgt_id SERIAL NOT NULL PRIMARY KEY ,
|
||||||
|
|
||||||
|
/* Name of the target, displayed in the administration interface */
|
||||||
|
updtgt_name VARCHAR( 32 ) NOT NULL ,
|
||||||
|
|
||||||
|
/* Schema in which the target table resides */
|
||||||
|
updtgt_schema NAME NOT NULL ,
|
||||||
|
|
||||||
|
/* Name of the target table */
|
||||||
|
updtgt_table NAME NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Update target names are unique */
|
||||||
|
CREATE UNIQUE INDEX idx_updtgt_name
|
||||||
|
ON sys.update_targets ( LOWER( updtgt_name ) );
|
||||||
|
/* Schema / table combinations are unique */
|
||||||
|
CREATE UNIQUE INDEX idx_updtgt_target
|
||||||
|
ON sys.update_targets ( updtgt_schema , updtgt_table );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update type definitions
|
||||||
|
* ------------------------
|
||||||
|
*
|
||||||
|
* An update type corresponds to a procedure which will be applied at each
|
||||||
|
* game update cycle (once per minute, unless defaults have been modified),
|
||||||
|
* and which performs a computation or set thereof on the game's data.
|
||||||
|
*/
|
||||||
|
DROP TABLE IF EXISTS sys.update_types CASCADE;
|
||||||
|
CREATE TABLE sys.update_types(
|
||||||
|
/* Automatically generated identifier of the update type */
|
||||||
|
updtype_id SERIAL NOT NULL ,
|
||||||
|
|
||||||
|
/* The type of target this update refers to */
|
||||||
|
updtgt_id INT NOT NULL ,
|
||||||
|
|
||||||
|
/* Name of the update type, used in the administration interface and when
|
||||||
|
* updates need to be executed externally.
|
||||||
|
*/
|
||||||
|
updtype_name VARCHAR( 64 ) NOT NULL ,
|
||||||
|
|
||||||
|
/* Ordering index of the update type. This field is always re-generated
|
||||||
|
* when update types are added or removed.
|
||||||
|
*/
|
||||||
|
updtype_ordering INT NOT NULL ,
|
||||||
|
|
||||||
|
/* Description of the update type to be included in the administration
|
||||||
|
* interface.
|
||||||
|
*/
|
||||||
|
updtype_description TEXT NOT NULL ,
|
||||||
|
|
||||||
|
/* Name of the stored procedure which handles the update. When this is
|
||||||
|
* NULL, the update type is assumed to be supported externally (i.e. by
|
||||||
|
* the game server) rather than internally. Otherwise, a stored procedure
|
||||||
|
* bearing that name, accepting a BIGINT as its parameter and returning
|
||||||
|
* VOID, must exist in the sys schema.
|
||||||
|
*/
|
||||||
|
updtype_proc_name NAME ,
|
||||||
|
|
||||||
|
/* Size of the update batch. If this is NULL, the global value from the
|
||||||
|
* game.batchSize constant will be used.
|
||||||
|
*/
|
||||||
|
updtype_batch_size INT ,
|
||||||
|
|
||||||
|
/* The primary key includes both the update type identifier and the target
|
||||||
|
* identifier for coherence reasons.
|
||||||
|
*/
|
||||||
|
PRIMARY KEY( updtype_id , updtgt_id ) ,
|
||||||
|
|
||||||
|
/* Batch sizes are either NULL or strictly positive */
|
||||||
|
CHECK( updtype_batch_size IS NULL
|
||||||
|
OR updtype_batch_size > 0 )
|
||||||
|
);
|
||||||
|
|
||||||
|
/* Update names must be unique, independently of the case */
|
||||||
|
CREATE UNIQUE INDEX idx_updtype_name
|
||||||
|
ON sys.update_types ( LOWER( updtype_name ) );
|
||||||
|
/* Update ordering index must be unique */
|
||||||
|
CREATE UNIQUE INDEX idx_updtype_ordering
|
||||||
|
ON sys.update_types ( updtype_ordering );
|
||||||
|
/* Procedure names must be unique */
|
||||||
|
CREATE UNIQUE INDEX idx_updtype_procname
|
||||||
|
ON sys.update_types ( updtype_proc_name );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ALTER TABLE sys.update_types
|
||||||
|
ADD CONSTRAINT fk_updtype_target
|
||||||
|
FOREIGN KEY ( updtgt_id ) REFERENCES sys.update_targets( updtgt_id );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update state type
|
||||||
|
* ------------------
|
||||||
|
*
|
||||||
|
* This type represents the possible states of a game update
|
||||||
|
*/
|
||||||
|
DROP TYPE IF EXISTS sys.update_state_type CASCADE;
|
||||||
|
CREATE TYPE sys.update_state_type
|
||||||
|
AS ENUM (
|
||||||
|
/* The row will be included in the current game update */
|
||||||
|
'FUTURE' ,
|
||||||
|
/* The row is being processed */
|
||||||
|
'PROCESSING' ,
|
||||||
|
/* The row has been processed by the current or previous game update */
|
||||||
|
'PROCESSED'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Main updates table
|
||||||
|
* -------------------
|
||||||
|
*
|
||||||
|
* This table lists all update rows, including their type and target, as well
|
||||||
|
* as their state.
|
||||||
|
*/
|
||||||
|
DROP TABLE IF EXISTS sys.updates CASCADE;
|
||||||
|
CREATE TABLE sys.updates(
|
||||||
|
/* The update row's automatically generated identifier */
|
||||||
|
update_id BIGSERIAL NOT NULL ,
|
||||||
|
|
||||||
|
/* The type of update this row is about */
|
||||||
|
updtype_id INT NOT NULL ,
|
||||||
|
|
||||||
|
/* The type of target for the update */
|
||||||
|
updtgt_id INT NOT NULL ,
|
||||||
|
|
||||||
|
/* The update row's current state */
|
||||||
|
update_state sys.update_state_type
|
||||||
|
NOT NULL
|
||||||
|
DEFAULT 'FUTURE' ,
|
||||||
|
|
||||||
|
/* The tick identifier corresponding to the last game update in which
|
||||||
|
* this row was processed.
|
||||||
|
*/
|
||||||
|
update_last BIGINT NOT NULL
|
||||||
|
DEFAULT -1 ,
|
||||||
|
|
||||||
|
/* The primary key includes the automatically generated identifier but
|
||||||
|
* also the type and target type.
|
||||||
|
*/
|
||||||
|
PRIMARY KEY( update_id , updtype_id , updtgt_id )
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE sys.updates
|
||||||
|
ADD CONSTRAINT fk_update_type
|
||||||
|
FOREIGN KEY ( updtype_id , updtgt_id ) REFERENCES sys.update_types
|
||||||
|
ON DELETE CASCADE;
|
|
@ -0,0 +1,571 @@
|
||||||
|
-- LegacyWorlds Beta 6
|
||||||
|
-- PostgreSQL database scripts
|
||||||
|
--
|
||||||
|
-- Game updates - support functions for update definitions
|
||||||
|
--
|
||||||
|
-- Copyright(C) 2004-2012, DeepClone Development
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Type that represents a field from a primary key
|
||||||
|
* ------------------------------------------------
|
||||||
|
*
|
||||||
|
* This type represents both the field itself (as a name) and its type.
|
||||||
|
*/
|
||||||
|
DROP TYPE IF EXISTS sys.pk_field_type CASCADE;
|
||||||
|
CREATE TYPE sys.pk_field_type AS (
|
||||||
|
f_name NAME ,
|
||||||
|
f_type NAME
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get a table's primary key fields and field types
|
||||||
|
* -------------------------------------------------
|
||||||
|
*
|
||||||
|
* This function will list all fields from a table's primary key along with
|
||||||
|
* their types.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* _schema_name The name of the schema the table resides in
|
||||||
|
* _table_name The name of the table
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* ? A set of sys.pk_field_type records
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.get_table_pkey( NAME , NAME ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.get_table_pkey( _schema_name NAME , _table_name NAME )
|
||||||
|
RETURNS SETOF sys.pk_field_type
|
||||||
|
LANGUAGE SQL
|
||||||
|
STRICT IMMUTABLE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $get_table_pkey$
|
||||||
|
|
||||||
|
SELECT _attr.attname AS f_name , _type.typname AS f_type
|
||||||
|
|
||||||
|
FROM pg_namespace _schema
|
||||||
|
INNER JOIN pg_class _table
|
||||||
|
ON _table.relnamespace = _schema.oid
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT indrelid , unnest( indkey ) AS indattnum
|
||||||
|
FROM pg_index WHERE indisprimary
|
||||||
|
) _index ON _index.indrelid = _table.oid
|
||||||
|
INNER JOIN pg_attribute _attr
|
||||||
|
ON _attr.attrelid = _table.oid
|
||||||
|
AND _attr.attnum = _index.indattnum
|
||||||
|
INNER JOIN pg_type _type
|
||||||
|
ON _type.oid = _attr.atttypid
|
||||||
|
|
||||||
|
WHERE _schema.nspname = $1
|
||||||
|
AND _table.relname = $2
|
||||||
|
|
||||||
|
ORDER BY _index.indattnum;
|
||||||
|
|
||||||
|
$get_table_pkey$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.get_table_pkey( NAME , NAME )
|
||||||
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Automatic update row insertion
|
||||||
|
* -------------------------------
|
||||||
|
*
|
||||||
|
* This trigger function is added to all type-specific update tables. It is
|
||||||
|
* called before insertions, and will make sure a generic update row exists
|
||||||
|
* for the new row.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.tgf_speupd_before_insert( ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.tgf_speupd_before_insert( )
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $tgf_speupd_before_insert$
|
||||||
|
|
||||||
|
DECLARE
|
||||||
|
_update_id BIGINT;
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
INSERT INTO sys.updates ( updtype_id , updtgt_id )
|
||||||
|
VALUES ( NEW.updtype_id , NEW.updtgt_id )
|
||||||
|
RETURNING update_id INTO _update_id;
|
||||||
|
|
||||||
|
NEW.update_id := _update_id;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$tgf_speupd_before_insert$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.tgf_speupd_before_insert( )
|
||||||
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Automatic update row removal
|
||||||
|
* -----------------------------
|
||||||
|
*
|
||||||
|
* This trigger function is added to all type-specific update tables. It is
|
||||||
|
* called once a row has been deleted, and will remove the corresponding
|
||||||
|
* generic update row.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.tgf_speupd_after_delete( ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.tgf_speupd_after_delete( )
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $tgf_speupd_after_delete$
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
DELETE FROM sys.updates
|
||||||
|
WHERE update_id = OLD.update_id
|
||||||
|
AND updtype_id = OLD.updtype_id
|
||||||
|
AND updtgt_id = OLD.updtgt_id;
|
||||||
|
|
||||||
|
RETURN OLD;
|
||||||
|
END;
|
||||||
|
$tgf_speupd_after_delete$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.tgf_speupd_after_delete( )
|
||||||
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add missing update rows
|
||||||
|
* ------------------------
|
||||||
|
*
|
||||||
|
* This function adds rows missing from the update and specific update tables
|
||||||
|
* for a given target type.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* _target_type The identifier of the target type for which missing
|
||||||
|
* rows are to be inserted.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.insert_missing_updates( INT ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.insert_missing_updates( _target_type INT )
|
||||||
|
RETURNS VOID
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $insert_missing_updates$
|
||||||
|
|
||||||
|
DECLARE
|
||||||
|
_t_schema TEXT;
|
||||||
|
_t_table TEXT;
|
||||||
|
_field_list TEXT;
|
||||||
|
_query TEXT;
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
SELECT INTO _t_schema , _t_table updtgt_schema , updtgt_table
|
||||||
|
FROM sys.update_targets
|
||||||
|
WHERE updtgt_id = _target_type;
|
||||||
|
SELECT INTO _field_list array_to_string( array_agg( f_name ) , ' , ' )
|
||||||
|
FROM sys.get_table_pkey( _t_schema , _t_table );
|
||||||
|
|
||||||
|
_query := 'INSERT INTO "' || _t_schema || '"."' || _t_table || '_updates" '
|
||||||
|
|| '( updtgt_id , updtype_id , ' || _field_list || ') '
|
||||||
|
|| 'SELECT updtgt_id , updtype_id , ' || _field_list
|
||||||
|
|| ' FROM sys.update_types CROSS JOIN "'
|
||||||
|
|| _t_schema || '"."' || _t_table || '" LEFT OUTER JOIN "'
|
||||||
|
|| _t_schema || '"."' || _t_table
|
||||||
|
|| '_updates" USING ( updtgt_id , updtype_id , ' || _field_list
|
||||||
|
|| ') WHERE update_id IS NULL AND updtgt_id = ' || _target_type;
|
||||||
|
EXECUTE _query;
|
||||||
|
|
||||||
|
END;
|
||||||
|
$insert_missing_updates$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.insert_missing_updates( INT )
|
||||||
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trigger function that adds missing update rows
|
||||||
|
* ----------------------------------------------
|
||||||
|
*
|
||||||
|
* This function calls sys.insert_missing_updates. The main difference between
|
||||||
|
* the original function and this one is that the former cannot be used in
|
||||||
|
* triggers, while the latter is meant to be used as a trigger on the target
|
||||||
|
* table.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.tgf_insert_missing_updates( ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.tgf_insert_missing_updates( )
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $tgf_insert_missing_updates$
|
||||||
|
BEGIN
|
||||||
|
IF TG_NARGS <> 1 THEN
|
||||||
|
RAISE EXCEPTION 'This trigger function requires one argument';
|
||||||
|
END IF;
|
||||||
|
PERFORM sys.insert_missing_updates( TG_ARGV[ 0 ]::INT );
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$tgf_insert_missing_updates$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.tgf_insert_missing_updates( )
|
||||||
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle new update types
|
||||||
|
* ------------------------
|
||||||
|
*
|
||||||
|
* This function is triggered when a new update type is created; it will insert
|
||||||
|
* the update rows for the new type.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.tgf_updtype_after_insert_row( ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.tgf_updtype_after_insert_row( )
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $tgf_updtype_after_insert$
|
||||||
|
BEGIN
|
||||||
|
PERFORM sys.insert_missing_updates( NEW.updtgt_id );
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$tgf_updtype_after_insert$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.tgf_updtype_after_insert_row( )
|
||||||
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
CREATE TRIGGER tg_updtype_after_insert_row
|
||||||
|
AFTER INSERT ON sys.update_types
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE sys.tgf_updtype_after_insert_row( );
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update type definition check
|
||||||
|
* -----------------------------
|
||||||
|
*
|
||||||
|
* This trigger function is called when a new update type is added or when
|
||||||
|
* an update type's stored procedure is updated. It makes sure that the
|
||||||
|
* corresponding stored procedure actually exists.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.tgf_check_update_type_proc( ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.tgf_check_update_type_proc( )
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $tgf_check_update_type_proc$
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
IF NEW.updtype_proc_name IS NULL THEN
|
||||||
|
RETURN NEW;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
PERFORM 1
|
||||||
|
FROM pg_namespace _schema
|
||||||
|
INNER JOIN pg_proc _proc
|
||||||
|
ON _proc.pronamespace = _schema.oid
|
||||||
|
WHERE _schema.nspname = 'sys'
|
||||||
|
AND _proc.proname = NEW.updtype_proc_name;
|
||||||
|
IF NOT FOUND THEN
|
||||||
|
RAISE EXCEPTION 'Update type % has invalid update function sys.%' ,
|
||||||
|
NEW.updtype_name , NEW.updtype_proc_name;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$tgf_check_update_type_proc$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.tgf_check_update_type_proc( )
|
||||||
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
CREATE TRIGGER tg_check_update_type_proc_insert
|
||||||
|
BEFORE INSERT ON sys.update_types
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE sys.tgf_check_update_type_proc( );
|
||||||
|
CREATE TRIGGER tg_check_update_type_proc_update
|
||||||
|
BEFORE UPDATE OF updtype_proc_name ON sys.update_types
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE sys.tgf_check_update_type_proc( );
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trigger that reorders the update types
|
||||||
|
* ---------------------------------------
|
||||||
|
*
|
||||||
|
* This function will update the ordering field of update types whenever rows
|
||||||
|
* are inserted or deleted.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.tgf_reorder_update_types( ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.tgf_reorder_update_types( )
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $tgf_reorder_update_types$
|
||||||
|
|
||||||
|
DECLARE
|
||||||
|
_max_ordering INT;
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
SELECT INTO _max_ordering MAX( updtype_ordering ) FROM sys.update_types;
|
||||||
|
IF _max_ordering IS NOT NULL THEN
|
||||||
|
UPDATE sys.update_types
|
||||||
|
SET updtype_ordering = updtype_ordering + _max_ordering * 2;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
UPDATE sys.update_types
|
||||||
|
SET updtype_ordering = updtype_rownumber * 2
|
||||||
|
FROM (
|
||||||
|
SELECT updtype_id AS id,
|
||||||
|
row_number( ) OVER (
|
||||||
|
ORDER BY updtype_ordering
|
||||||
|
) AS updtype_rownumber
|
||||||
|
FROM sys.update_types
|
||||||
|
) _row
|
||||||
|
WHERE updtype_id = _row.id;
|
||||||
|
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$tgf_reorder_update_types$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.tgf_reorder_update_types( )
|
||||||
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
CREATE TRIGGER tg_updtype_after_insert_stmt
|
||||||
|
AFTER INSERT ON sys.update_types
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE sys.tgf_reorder_update_types( );
|
||||||
|
|
||||||
|
CREATE TRIGGER tg_updtype_after_delete_stmt
|
||||||
|
AFTER DELETE ON sys.update_types
|
||||||
|
FOR EACH STATEMENT EXECUTE PROCEDURE sys.tgf_reorder_update_types( );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check target tables
|
||||||
|
* --------------------
|
||||||
|
*
|
||||||
|
* Before a new row is inserted into sys.update_targets, make sure
|
||||||
|
* the target table it contains actually exists.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.tgf_updtgt_before_insert( ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.tgf_updtgt_before_insert( )
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $tgf_updtgt_before_insert$
|
||||||
|
BEGIN
|
||||||
|
PERFORM 1
|
||||||
|
FROM pg_class pc
|
||||||
|
INNER JOIN pg_namespace pn
|
||||||
|
ON pn.oid = pc.relnamespace
|
||||||
|
INNER JOIN pg_roles pr
|
||||||
|
ON pr.oid = pc.relowner
|
||||||
|
WHERE pn.nspname = NEW.updtgt_schema
|
||||||
|
AND pc.relname = NEW.updtgt_table
|
||||||
|
AND pc.reltype <> 0 AND pc.relkind = 'r'
|
||||||
|
AND pr.rolname <> 'postgres';
|
||||||
|
IF NOT FOUND THEN
|
||||||
|
RAISE EXCEPTION 'Update target table %.% not found' ,
|
||||||
|
NEW.updtgt_schema , NEW.updtgt_table
|
||||||
|
USING ERRCODE = 'foreign_key_violation';
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$tgf_updtgt_before_insert$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.tgf_updtgt_before_insert( )
|
||||||
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
CREATE TRIGGER tg_updtgt_before_insert
|
||||||
|
BEFORE INSERT ON sys.update_targets
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE sys.tgf_updtgt_before_insert( );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create update list and triggers for target table
|
||||||
|
* -------------------------------------------------
|
||||||
|
*
|
||||||
|
* After a new update type has been registered, a table listing updates
|
||||||
|
* as well as a set of triggers on the target table need to be created.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.tgf_updtgt_after_insert( ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.tgf_updtgt_after_insert( )
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE
|
||||||
|
AS $tgf_updtgt_after_insert$
|
||||||
|
|
||||||
|
DECLARE
|
||||||
|
_table_name TEXT;
|
||||||
|
_query TEXT;
|
||||||
|
_field_name NAME;
|
||||||
|
_field_type NAME;
|
||||||
|
_field_list TEXT;
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
_table_name := '"' || NEW.updtgt_schema || '"."' || NEW.updtgt_table || '_updates"';
|
||||||
|
_query := 'CREATE TABLE ' || _table_name || $_table_base_fields$(
|
||||||
|
update_id BIGINT NOT NULL ,
|
||||||
|
updtype_id INT NOT NULL ,
|
||||||
|
updtgt_id INT NOT NULL
|
||||||
|
$_table_base_fields$;
|
||||||
|
|
||||||
|
-- List target table's primary key fields
|
||||||
|
_field_list := '';
|
||||||
|
FOR _field_name , _field_type IN
|
||||||
|
SELECT * FROM sys.get_table_pkey( NEW.updtgt_schema , NEW.updtgt_table )
|
||||||
|
LOOP
|
||||||
|
_query := _query || ', "' || _field_name || '" "' || _field_type
|
||||||
|
|| '" NOT NULL';
|
||||||
|
IF _field_list <> '' THEN
|
||||||
|
_field_list := _field_list || ' , ';
|
||||||
|
END IF;
|
||||||
|
_field_list := _field_list || '"' || _field_name || '"';
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
_query := _query ||
|
||||||
|
' , PRIMARY KEY ( updtgt_id , updtype_id , update_id , '
|
||||||
|
|| _field_list || ' ) , CHECK ( updtgt_id = ' || NEW.updtgt_id || ' ) )';
|
||||||
|
EXECUTE _query;
|
||||||
|
|
||||||
|
-- Add foreign keys
|
||||||
|
_query := 'ALTER TABLE ' || _table_name
|
||||||
|
|| ' ADD CONSTRAINT fk_upd_' || NEW.updtgt_table || '_update '
|
||||||
|
|| 'FOREIGN KEY ( update_id , updtype_id , updtgt_id ) REFERENCES sys.updates '
|
||||||
|
|| 'ON DELETE CASCADE , '
|
||||||
|
|| ' ADD CONSTRAINT fk_upd_' || NEW.updtgt_table || '_target '
|
||||||
|
|| 'FOREIGN KEY ( ' || _field_list
|
||||||
|
|| ' ) REFERENCES "' || NEW.updtgt_schema || '"."' || NEW.updtgt_table
|
||||||
|
|| '" ON DELETE CASCADE';
|
||||||
|
EXECUTE _query;
|
||||||
|
|
||||||
|
-- Create automatic update creation/deletion triggers
|
||||||
|
_query := 'CREATE TRIGGER tg_speupd_before_insert BEFORE INSERT ON ' || _table_name
|
||||||
|
|| ' FOR EACH ROW EXECUTE PROCEDURE sys.tgf_speupd_before_insert( )';
|
||||||
|
EXECUTE _query;
|
||||||
|
_query := 'CREATE TRIGGER tg_speupd_after_delete AFTER DELETE ON ' || _table_name
|
||||||
|
|| ' FOR EACH ROW EXECUTE PROCEDURE sys.tgf_speupd_after_delete( )';
|
||||||
|
EXECUTE _query;
|
||||||
|
|
||||||
|
-- Create triggers that will insert/delete update rows when new items are added/removed
|
||||||
|
_query := 'CREATE TRIGGER tg_speupd_after_insert AFTER INSERT ON "'
|
||||||
|
|| NEW.updtgt_schema || '"."' || NEW.updtgt_table
|
||||||
|
|| '" FOR EACH STATEMENT EXECUTE PROCEDURE sys.tgf_insert_missing_updates( '
|
||||||
|
|| NEW.updtgt_id || ' )';
|
||||||
|
EXECUTE _query;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$tgf_updtgt_after_insert$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.tgf_updtgt_after_insert( )
|
||||||
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
CREATE TRIGGER tg_updtgt_after_insert
|
||||||
|
AFTER INSERT ON sys.update_targets
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE sys.tgf_updtgt_after_insert( );
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Trigger function that deletes specific update tables
|
||||||
|
* -----------------------------------------------------
|
||||||
|
*
|
||||||
|
* This trigger function is called after a row has been deleted from the
|
||||||
|
* update target definitions table. It will destroy the corresponding table.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.tgf_updtgt_after_delete( );
|
||||||
|
CREATE FUNCTION sys.tgf_updtgt_after_delete( )
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $tgf_updtgt_after_delete$
|
||||||
|
DECLARE
|
||||||
|
_query TEXT;
|
||||||
|
BEGIN
|
||||||
|
_query := 'DROP TABLE "' || OLD.updtgt_schema || '"."' || OLD.updtgt_table || '_updates"';
|
||||||
|
EXECUTE _query;
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$tgf_updtgt_after_delete$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.tgf_updtgt_after_delete( )
|
||||||
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
CREATE TRIGGER tg_updtgt_after_delete
|
||||||
|
AFTER DELETE ON sys.update_targets
|
||||||
|
FOR EACH ROW EXECUTE PROCEDURE sys.tgf_updtgt_after_delete( );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stored update type registration
|
||||||
|
* --------------------------------
|
||||||
|
*
|
||||||
|
* This function can be called to register an update type that uses a stored
|
||||||
|
* procedure. The new update type is added at the end of the list of updates.
|
||||||
|
*
|
||||||
|
* Since this function is meant to be used from the SQL definition code only,
|
||||||
|
* it does not handle errors and will raise exceptions when something goes
|
||||||
|
* wrong.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* _target The name of the target for this update type
|
||||||
|
* _name The name of the update type
|
||||||
|
* _description The update type's description
|
||||||
|
* _proc The name of the stored procedure
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.register_update_type( TEXT , TEXT , TEXT , NAME );
|
||||||
|
CREATE FUNCTION sys.register_update_type( _target TEXT , _name TEXT , _description TEXT , _proc NAME )
|
||||||
|
RETURNS VOID
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
STRICT VOLATILE
|
||||||
|
SECURITY INVOKER
|
||||||
|
AS $register_update_type$
|
||||||
|
|
||||||
|
DECLARE
|
||||||
|
_target_id INT;
|
||||||
|
_max_ordering INT;
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
SELECT INTO _target_id updtgt_id
|
||||||
|
FROM sys.update_targets
|
||||||
|
WHERE updtgt_name = _target;
|
||||||
|
|
||||||
|
SELECT INTO _max_ordering MAX( updtype_ordering )
|
||||||
|
FROM sys.update_types;
|
||||||
|
IF _max_ordering IS NULL THEN
|
||||||
|
_max_ordering := 1;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
INSERT INTO sys.update_types(
|
||||||
|
updtgt_id , updtype_name , updtype_ordering ,
|
||||||
|
updtype_description , updtype_proc_name
|
||||||
|
) VALUES (
|
||||||
|
_target_id , _name , _max_ordering + 1 ,
|
||||||
|
_description , _proc
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
$register_update_type$;
|
||||||
|
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.register_update_type( TEXT , TEXT , TEXT , NAME )
|
||||||
|
FROM PUBLIC;
|
|
@ -45,18 +45,6 @@ BEGIN
|
||||||
-- Add empire resources
|
-- Add empire resources
|
||||||
INSERT INTO emp.resources ( empire_id , resource_name_id )
|
INSERT INTO emp.resources ( empire_id , resource_name_id )
|
||||||
SELECT _name_id , resource_name_id FROM defs.resources;
|
SELECT _name_id , resource_name_id FROM defs.resources;
|
||||||
|
|
||||||
-- Add empire update records
|
|
||||||
FOR _update_type IN SELECT _type
|
|
||||||
FROM unnest( enum_range( NULL::update_type ) ) AS _type
|
|
||||||
WHERE _type::text LIKE 'EMPIRE_%'
|
|
||||||
LOOP
|
|
||||||
INSERT INTO sys.updates( gu_type )
|
|
||||||
VALUES ( _update_type )
|
|
||||||
RETURNING id INTO _update;
|
|
||||||
INSERT INTO emp.updates ( update_id , empire_id )
|
|
||||||
VALUES ( _update , _name_id );
|
|
||||||
END LOOP;
|
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
|
|
@ -196,17 +196,6 @@ BEGIN
|
||||||
-- FIXME: for now, just stick data about resources in the appropriate table
|
-- FIXME: for now, just stick data about resources in the appropriate table
|
||||||
INSERT INTO verse.planet_resources ( planet_id , resource_name_id )
|
INSERT INTO verse.planet_resources ( planet_id , resource_name_id )
|
||||||
SELECT pnid , resource_name_id FROM defs.resources;
|
SELECT pnid , resource_name_id FROM defs.resources;
|
||||||
|
|
||||||
-- Add planet update records
|
|
||||||
FOR utp IN SELECT x FROM unnest( enum_range( NULL::update_type ) ) AS x
|
|
||||||
WHERE x::text LIKE 'PLANET_%'
|
|
||||||
LOOP
|
|
||||||
INSERT INTO sys.updates( gu_type )
|
|
||||||
VALUES ( utp )
|
|
||||||
RETURNING id INTO uid;
|
|
||||||
INSERT INTO verse.updates ( update_id , planet_id )
|
|
||||||
VALUES ( uid , pnid );
|
|
||||||
END LOOP;
|
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
|
|
@ -8,17 +8,29 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--
|
/*
|
||||||
-- Start a tick
|
* Start a game update cycle
|
||||||
--
|
* --------------------------
|
||||||
|
*
|
||||||
CREATE OR REPLACE FUNCTION sys.start_tick( OUT tick_id BIGINT )
|
* This function prepares the execution of a cycle of game updates. It will
|
||||||
|
* try to find the identifier of the next update, and mark all game updates
|
||||||
|
* as requiring an update for this identifier.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* tick_id The identifier of the new update cycle, or NULL if an
|
||||||
|
* update was already in progress.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.start_tick( ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.start_tick( OUT tick_id BIGINT )
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
STRICT VOLATILE
|
STRICT VOLATILE
|
||||||
SECURITY DEFINER
|
SECURITY DEFINER
|
||||||
AS $$
|
AS $start_tick$
|
||||||
|
|
||||||
DECLARE
|
DECLARE
|
||||||
n_tick BIGINT;
|
n_tick BIGINT;
|
||||||
c_tick BIGINT;
|
c_tick BIGINT;
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Get next / current tick
|
-- Get next / current tick
|
||||||
SELECT INTO n_tick , c_tick next_tick , current_tick
|
SELECT INTO n_tick , c_tick next_tick , current_tick
|
||||||
|
@ -31,17 +43,27 @@ BEGIN
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Prepare game updates
|
-- Prepare game updates
|
||||||
UPDATE sys.updates SET last_tick = n_tick , status = 'FUTURE'
|
UPDATE sys.updates
|
||||||
WHERE last_tick < n_tick;
|
SET update_last = n_tick ,
|
||||||
|
update_state = 'FUTURE'
|
||||||
|
WHERE update_last < n_tick;
|
||||||
|
|
||||||
-- Update system status
|
-- Update system status
|
||||||
UPDATE sys.status SET current_tick = n_tick , next_tick = n_tick + 1;
|
UPDATE sys.status
|
||||||
|
SET current_tick = n_tick ,
|
||||||
|
next_tick = n_tick + 1;
|
||||||
|
|
||||||
tick_id := n_tick;
|
tick_id := n_tick;
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
GRANT EXECUTE ON FUNCTION sys.start_tick( ) TO :dbuser;
|
END;
|
||||||
|
$start_tick$;
|
||||||
|
|
||||||
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.start_tick( )
|
||||||
|
FROM PUBLIC;
|
||||||
|
GRANT EXECUTE
|
||||||
|
ON FUNCTION sys.start_tick( )
|
||||||
|
TO :dbuser;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,17 +86,29 @@ $$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--
|
/*
|
||||||
-- Check if a tick got "stuck"
|
* Check if a game update cycle got "stuck"
|
||||||
--
|
* -----------------------------------------
|
||||||
|
*
|
||||||
CREATE OR REPLACE FUNCTION sys.check_stuck_tick( OUT tick_id BIGINT )
|
* Check sys.status for an in-progress game update identifier. If there is
|
||||||
|
* one, check if there are further updates to execute in the cycle in
|
||||||
|
* question.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* tick_id The stuck tick's identifier, or NULL if there is no stuck
|
||||||
|
* tick or if maintenance mode is enabled
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.check_stuck_tick( ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.check_stuck_tick( OUT tick_id BIGINT )
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
STRICT VOLATILE
|
STRICT VOLATILE
|
||||||
SECURITY DEFINER
|
SECURITY DEFINER
|
||||||
AS $$
|
AS $check_stuck_tick$
|
||||||
|
|
||||||
DECLARE
|
DECLARE
|
||||||
c_tick BIGINT;
|
c_tick BIGINT;
|
||||||
u_count INT;
|
u_count INT;
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Get next / current tick
|
-- Get next / current tick
|
||||||
SELECT INTO c_tick current_tick
|
SELECT INTO c_tick current_tick
|
||||||
|
@ -87,71 +121,123 @@ BEGIN
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- Are there any updates left?
|
-- Are there any updates left?
|
||||||
SELECT INTO u_count count(*) FROM sys.updates
|
SELECT INTO u_count count(*)
|
||||||
WHERE status = 'FUTURE' AND last_tick = c_tick;
|
FROM sys.updates
|
||||||
|
WHERE update_state = 'FUTURE'
|
||||||
|
AND update_last = c_tick;
|
||||||
IF u_count = 0 THEN
|
IF u_count = 0 THEN
|
||||||
PERFORM sys.end_tick( c_tick );
|
PERFORM sys.end_tick( c_tick );
|
||||||
tick_id := NULL;
|
tick_id := NULL;
|
||||||
ELSE
|
ELSE
|
||||||
tick_id := c_tick;
|
tick_id := c_tick;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$check_stuck_tick$;
|
||||||
|
|
||||||
GRANT EXECUTE ON FUNCTION sys.check_stuck_tick( ) TO :dbuser;
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.check_stuck_tick( )
|
||||||
|
FROM PUBLIC;
|
||||||
|
GRANT EXECUTE
|
||||||
|
ON FUNCTION sys.check_stuck_tick( )
|
||||||
|
TO :dbuser;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--
|
/*
|
||||||
-- Process game updates
|
* Process game updates
|
||||||
--
|
* ---------------------
|
||||||
-- Parameters:
|
*
|
||||||
-- c_tick Current tick
|
* This function checks for game update elements, marking some of them for
|
||||||
--
|
* processing depending on their type. If there are no more updates to run,
|
||||||
-- Returns:
|
* end the update cycle; otherwise, depending on the type of update, process
|
||||||
-- TRUE if the function must be called again, FALSE otherwise
|
* the items internally or return a value that indicates they are to be
|
||||||
--
|
* processed by the external Java code.
|
||||||
|
*
|
||||||
CREATE OR REPLACE FUNCTION sys.process_updates( IN c_tick BIGINT , OUT has_more BOOLEAN )
|
* Parameters:
|
||||||
|
* _current_update Current game update's identifier
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* _has_more TRUE if the function must be called again, FALSE
|
||||||
|
* otherwise
|
||||||
|
* _process_externally NULL if there is no update to process or if the
|
||||||
|
* updates were processed in the database, or
|
||||||
|
* the name of the update type if external
|
||||||
|
* processing is needed.
|
||||||
|
*/
|
||||||
|
DROP FUNCTION IF EXISTS sys.process_updates( BIGINT ) CASCADE;
|
||||||
|
CREATE FUNCTION sys.process_updates(
|
||||||
|
IN _current_update BIGINT ,
|
||||||
|
OUT _has_more BOOLEAN ,
|
||||||
|
OUT _process_externally TEXT )
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
STRICT VOLATILE
|
STRICT VOLATILE
|
||||||
SECURITY DEFINER
|
SECURITY DEFINER
|
||||||
AS $$
|
AS $process_updates$
|
||||||
|
|
||||||
DECLARE
|
DECLARE
|
||||||
b_size INT;
|
_current_type INT;
|
||||||
p_utype update_type;
|
_batch_size INT;
|
||||||
utype update_type;
|
_type_name TEXT;
|
||||||
uid BIGINT;
|
_proc_name NAME;
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
b_size := sys.get_constant( 'game.batchSize' );
|
-- Mark all entries that were being processed as having been processed
|
||||||
p_utype := NULL;
|
UPDATE sys.updates
|
||||||
|
SET update_state = 'PROCESSED'
|
||||||
|
WHERE update_state = 'PROCESSING'
|
||||||
|
AND update_last = _current_update;
|
||||||
|
|
||||||
-- Mark at most b_size entries as being updated
|
-- Find the next type of update to process and its specific parameters
|
||||||
FOR uid , utype IN SELECT id , gu_type FROM sys.updates
|
SELECT INTO _current_type , _batch_size , _type_name , _proc_name
|
||||||
WHERE last_tick = c_tick AND status = 'FUTURE'
|
updtype_id , updtype_batch_size , updtype_name , updtype_proc_name
|
||||||
ORDER BY gu_type LIMIT b_size
|
FROM sys.update_types
|
||||||
LOOP
|
INNER JOIN sys.updates
|
||||||
IF p_utype IS NULL THEN
|
USING ( updtype_id , updtgt_id )
|
||||||
p_utype := utype;
|
WHERE update_state = 'FUTURE'
|
||||||
|
AND update_last = _current_update
|
||||||
|
ORDER BY updtype_ordering
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
_has_more := FOUND;
|
||||||
|
IF _has_more THEN
|
||||||
|
-- Check batch size
|
||||||
|
IF _batch_size IS NULL THEN
|
||||||
|
_batch_size := sys.get_constant( 'game.batchSize' );
|
||||||
END IF;
|
END IF;
|
||||||
EXIT WHEN utype <> p_utype;
|
|
||||||
UPDATE sys.updates SET status = 'PROCESSING' WHERE id = uid;
|
|
||||||
END LOOP;
|
|
||||||
|
|
||||||
has_more := p_utype IS NOT NULL;
|
-- Mark at most _batch_size entries of the right type as being updated
|
||||||
IF has_more THEN
|
UPDATE sys.updates
|
||||||
-- Execute actual updates
|
SET update_state = 'PROCESSING'
|
||||||
EXECUTE 'SELECT sys.process_' || lower( p_utype::TEXT ) || '_updates( $1 )'
|
WHERE update_id IN (
|
||||||
USING c_tick;
|
SELECT update_id FROM sys.updates
|
||||||
UPDATE sys.updates SET status = 'PROCESSED'
|
WHERE updtype_id = _current_type
|
||||||
WHERE status = 'PROCESSING' AND last_tick = c_tick;
|
AND update_state = 'FUTURE'
|
||||||
|
AND update_last = _current_update
|
||||||
|
LIMIT _batch_size
|
||||||
|
);
|
||||||
|
|
||||||
|
IF _proc_name IS NULL THEN
|
||||||
|
-- External processing is required
|
||||||
|
_process_externally := _type_name;
|
||||||
ELSE
|
ELSE
|
||||||
-- If nothing was found, we're done
|
-- Process updates using a stored procedure
|
||||||
PERFORM sys.end_tick( c_tick );
|
EXECUTE 'SELECT sys."' || _proc_name || '"( $1 )'
|
||||||
|
USING _current_update;
|
||||||
|
END IF;
|
||||||
|
ELSE
|
||||||
|
-- No updates left to run
|
||||||
|
PERFORM sys.end_tick( _current_update );
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$process_updates$;
|
||||||
|
|
||||||
GRANT EXECUTE ON FUNCTION sys.process_updates( BIGINT ) TO :dbuser;
|
REVOKE EXECUTE
|
||||||
|
ON FUNCTION sys.process_updates( BIGINT )
|
||||||
|
FROM PUBLIC;
|
||||||
|
GRANT EXECUTE
|
||||||
|
ON FUNCTION sys.process_updates( BIGINT )
|
||||||
|
TO :dbuser;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
-- LegacyWorlds Beta 6
|
||||||
|
-- PostgreSQL database scripts
|
||||||
|
--
|
||||||
|
-- Game updates - core update targets
|
||||||
|
--
|
||||||
|
-- Copyright(C) 2004-2012, DeepClone Development
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
INSERT INTO sys.update_targets (
|
||||||
|
updtgt_name , updtgt_schema , updtgt_table
|
||||||
|
) VALUES (
|
||||||
|
'Planets' , 'verse' , 'planets'
|
||||||
|
) , (
|
||||||
|
'Empires' , 'emp' , 'empires'
|
||||||
|
);
|
||||||
|
|
|
@ -18,13 +18,14 @@ DECLARE
|
||||||
c_debt REAL;
|
c_debt REAL;
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Lock empires for update
|
-- Lock empires for update
|
||||||
PERFORM e.name_id FROM sys.updates su
|
PERFORM e.name_id
|
||||||
INNER JOIN emp.updates eu
|
FROM sys.updates su
|
||||||
ON eu.update_id = su.id
|
INNER JOIN emp.empires_updates eu
|
||||||
|
USING ( updtgt_id , updtype_id , update_id )
|
||||||
INNER JOIN emp.empires e
|
INNER JOIN emp.empires e
|
||||||
ON eu.empire_id = e.name_id
|
USING ( name_id )
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick
|
||||||
AND su.gu_type = 'EMPIRE_MONEY'
|
AND su.update_state = 'PROCESSING'
|
||||||
FOR UPDATE OF e;
|
FOR UPDATE OF e;
|
||||||
|
|
||||||
-- Select all money-related data from empires being updated
|
-- Select all money-related data from empires being updated
|
||||||
|
@ -32,14 +33,16 @@ BEGIN
|
||||||
( pov.planet_income - pov.planet_upkeep ) AS p_money ,
|
( pov.planet_income - pov.planet_upkeep ) AS p_money ,
|
||||||
fov.fleet_upkeep AS f_money , ( v.status = 'PROCESSED' ) AS on_vacation
|
fov.fleet_upkeep AS f_money , ( v.status = 'PROCESSED' ) AS on_vacation
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN emp.updates eu ON eu.update_id = su.id
|
INNER JOIN emp.empires_updates eu
|
||||||
INNER JOIN emp.empires e ON eu.empire_id = e.name_id
|
USING ( updtgt_id , updtype_id , update_id )
|
||||||
|
INNER JOIN emp.empires e
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN emp.fleets_overview fov ON fov.empire = e.name_id
|
INNER JOIN emp.fleets_overview fov ON fov.empire = e.name_id
|
||||||
INNER JOIN emp.planets_overview pov ON pov.empire = e.name_id
|
INNER JOIN emp.planets_overview pov ON pov.empire = e.name_id
|
||||||
INNER JOIN naming.empire_names en ON en.id = e.name_id
|
INNER JOIN naming.empire_names en ON en.id = e.name_id
|
||||||
LEFT OUTER JOIN users.vacations v ON v.account_id = en.owner_id
|
LEFT OUTER JOIN users.vacations v ON v.account_id = en.owner_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick
|
||||||
AND su.gu_type = 'EMPIRE_MONEY'
|
AND su.update_state = 'PROCESSING'
|
||||||
LOOP
|
LOOP
|
||||||
-- Compute new cash reserve
|
-- Compute new cash reserve
|
||||||
c_cash := 0;
|
c_cash := 0;
|
||||||
|
@ -79,3 +82,11 @@ BEGIN
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Empires' , 'EmpireMoney' ,
|
||||||
|
'Empires'' money is being updated using the previous update''s results. '
|
||||||
|
|| 'This update type should disappear, as everything it does will '
|
||||||
|
|| 'be replaced by the resources update.' ,
|
||||||
|
'process_empire_money_updates'
|
||||||
|
);
|
|
@ -42,17 +42,16 @@ AS $process_empire_resources_updates$
|
||||||
END
|
END
|
||||||
|
|
||||||
FROM sys.updates _upd_sys
|
FROM sys.updates _upd_sys
|
||||||
INNER JOIN emp.updates _upd_emp
|
INNER JOIN emp.empires_updates _upd_emp
|
||||||
ON _upd_emp.update_id = _upd_sys.id
|
USING ( updtgt_id , updtype_id , update_id )
|
||||||
INNER JOIN emp.planets _emp_planets
|
INNER JOIN emp.planets _emp_planets
|
||||||
USING ( empire_id )
|
ON empire_id = name_id
|
||||||
INNER JOIN verse.planet_resources _pl_resources
|
INNER JOIN verse.planet_resources _pl_resources
|
||||||
USING ( planet_id )
|
USING ( planet_id )
|
||||||
|
|
||||||
WHERE _upd_sys.last_tick = $1
|
WHERE _upd_sys.update_last = $1
|
||||||
AND _upd_sys.status = 'PROCESSING'
|
AND _upd_sys.update_state = 'PROCESSING'
|
||||||
AND _upd_sys.gu_type = 'EMPIRE_RESOURCES'
|
AND _emp_resources.empire_id = _upd_emp.name_id
|
||||||
AND _emp_resources.empire_id = _upd_emp.empire_id
|
|
||||||
AND _emp_resources.resource_name_id = _pl_resources.resource_name_id;
|
AND _emp_resources.resource_name_id = _pl_resources.resource_name_id;
|
||||||
|
|
||||||
$process_empire_resources_updates$ LANGUAGE SQL;
|
$process_empire_resources_updates$ LANGUAGE SQL;
|
||||||
|
@ -61,3 +60,10 @@ $process_empire_resources_updates$ LANGUAGE SQL;
|
||||||
REVOKE EXECUTE
|
REVOKE EXECUTE
|
||||||
ON FUNCTION sys.process_empire_resources_updates( BIGINT )
|
ON FUNCTION sys.process_empire_resources_updates( BIGINT )
|
||||||
FROM PUBLIC;
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Empires' , 'EmpireResources' ,
|
||||||
|
'Empires'' resources are being updated using the previous update''s results. ' ,
|
||||||
|
'process_empire_resources_updates'
|
||||||
|
);
|
|
@ -18,28 +18,31 @@ DECLARE
|
||||||
tu_rec RECORD;
|
tu_rec RECORD;
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Lock empires for update and planets for share
|
-- Lock empires for update and planets for share
|
||||||
PERFORM e.name_id FROM sys.updates su
|
PERFORM e.name_id
|
||||||
INNER JOIN emp.updates eu ON eu.update_id = su.id
|
FROM sys.updates _upd_sys
|
||||||
INNER JOIN emp.empires e ON eu.empire_id = e.name_id
|
INNER JOIN emp.empires_updates eu
|
||||||
|
USING ( updtgt_id , updtype_id , update_id )
|
||||||
|
INNER JOIN emp.empires e USING ( name_id )
|
||||||
INNER JOIN emp.planets ep ON ep.empire_id = e.name_id
|
INNER JOIN emp.planets ep ON ep.empire_id = e.name_id
|
||||||
INNER JOIN verse.planets p ON p.name_id = ep.planet_id
|
INNER JOIN verse.planets p ON p.name_id = ep.planet_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE _upd_sys.update_last = c_tick
|
||||||
AND su.gu_type = 'EMPIRE_RESEARCH'
|
AND _upd_sys.update_state = 'PROCESSING'
|
||||||
FOR UPDATE OF e
|
FOR UPDATE OF e
|
||||||
FOR SHARE OF ep , p;
|
FOR SHARE OF ep , p;
|
||||||
|
|
||||||
-- Process empires
|
-- Process empires
|
||||||
FOR rec IN SELECT e.name_id AS id , ( v.status = 'PROCESSED' ) AS on_vacation ,
|
FOR rec IN SELECT e.name_id AS id , ( v.status = 'PROCESSED' ) AS on_vacation ,
|
||||||
sum( p.population ) AS population
|
sum( p.population ) AS population
|
||||||
FROM sys.updates su
|
FROM sys.updates _upd_sys
|
||||||
INNER JOIN emp.updates eu ON eu.update_id = su.id
|
INNER JOIN emp.empires_updates eu
|
||||||
INNER JOIN emp.empires e ON eu.empire_id = e.name_id
|
USING ( updtgt_id , updtype_id , update_id )
|
||||||
|
INNER JOIN emp.empires e USING ( name_id )
|
||||||
INNER JOIN emp.planets ep ON ep.empire_id = e.name_id
|
INNER JOIN emp.planets ep ON ep.empire_id = e.name_id
|
||||||
INNER JOIN verse.planets p ON p.name_id = ep.planet_id
|
INNER JOIN verse.planets p ON p.name_id = ep.planet_id
|
||||||
INNER JOIN naming.empire_names en ON en.id = e.name_id
|
INNER JOIN naming.empire_names en ON en.id = e.name_id
|
||||||
LEFT OUTER JOIN users.vacations v ON v.account_id = en.owner_id
|
LEFT OUTER JOIN users.vacations v ON v.account_id = en.owner_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE _upd_sys.update_last = c_tick
|
||||||
AND su.gu_type = 'EMPIRE_RESEARCH'
|
AND _upd_sys.update_state = 'PROCESSING'
|
||||||
GROUP BY e.name_id , v.status
|
GROUP BY e.name_id , v.status
|
||||||
LOOP
|
LOOP
|
||||||
-- Insert any missing tech line
|
-- Insert any missing tech line
|
||||||
|
@ -84,3 +87,8 @@ BEGIN
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Empires' , 'EmpireResearch' ,
|
||||||
|
'Empire research points are being attributed to technologies.' ,
|
||||||
|
'process_empire_research_updates'
|
||||||
|
);
|
|
@ -25,11 +25,13 @@ BEGIN
|
||||||
bld_dr := sys.get_constant( 'game.debt.buildings');
|
bld_dr := sys.get_constant( 'game.debt.buildings');
|
||||||
|
|
||||||
FOR empire, debt IN SELECT e.name_id AS id , e.debt
|
FOR empire, debt IN SELECT e.name_id AS id , e.debt
|
||||||
FROM sys.updates su
|
FROM sys.updates _upd_sys
|
||||||
INNER JOIN emp.updates eu ON eu.update_id = su.id
|
INNER JOIN emp.empires_updates eu
|
||||||
INNER JOIN emp.empires e ON eu.empire_id = e.name_id
|
USING ( updtgt_id , updtype_id , update_id )
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
INNER JOIN emp.empires e USING ( name_id )
|
||||||
AND su.gu_type = 'EMPIRE_DEBT' AND e.debt > 0
|
WHERE _upd_sys.update_last = c_tick
|
||||||
|
AND _upd_sys.update_state = 'PROCESSING'
|
||||||
|
AND e.debt > 0
|
||||||
FOR UPDATE
|
FOR UPDATE
|
||||||
LOOP
|
LOOP
|
||||||
PERFORM sys.write_log( 'EmpireDebt' , 'DEBUG'::log_level , 'Handling debt for empire #'
|
PERFORM sys.write_log( 'EmpireDebt' , 'DEBUG'::log_level , 'Handling debt for empire #'
|
||||||
|
@ -60,3 +62,8 @@ BEGIN
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Empires' , 'EmpireDebt' ,
|
||||||
|
'The effects of empires'' debts are being computed.' ,
|
||||||
|
'process_empire_debt_updates'
|
||||||
|
);
|
|
@ -7,7 +7,7 @@
|
||||||
-- --------------------------------------------------------
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION sys.process_planet_fleet_arrivals_updates( c_tick BIGINT )
|
CREATE OR REPLACE FUNCTION sys.process_fleet_arrivals_updates( c_tick BIGINT )
|
||||||
RETURNS VOID
|
RETURNS VOID
|
||||||
STRICT VOLATILE
|
STRICT VOLATILE
|
||||||
SECURITY INVOKER
|
SECURITY INVOKER
|
||||||
|
@ -18,14 +18,16 @@ DECLARE
|
||||||
f_ids BIGINT[];
|
f_ids BIGINT[];
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Lock all records
|
-- Lock all records
|
||||||
PERFORM f.id FROM sys.updates su
|
PERFORM 1
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
FROM sys.updates su
|
||||||
INNER JOIN verse.planets p ON p.name_id = vu.planet_id
|
INNER JOIN verse.planets_updates vu
|
||||||
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
||||||
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
||||||
INNER JOIN emp.empires e ON e.name_id = f.owner_id
|
INNER JOIN emp.empires e ON e.name_id = f.owner_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_FLEET_ARRIVALS'
|
|
||||||
AND f.status = 'AVAILABLE' AND fm.time_left = 1
|
AND f.status = 'AVAILABLE' AND fm.time_left = 1
|
||||||
FOR UPDATE;
|
FOR UPDATE;
|
||||||
|
|
||||||
|
@ -34,8 +36,10 @@ BEGIN
|
||||||
f.owner_id AS fleet_owner , ( v.status = 'PROCESSED' AND b.id IS NULL ) AS on_vacation ,
|
f.owner_id AS fleet_owner , ( v.status = 'PROCESSED' AND b.id IS NULL ) AS on_vacation ,
|
||||||
bool_or( f.attacking ) AS attacking
|
bool_or( f.attacking ) AS attacking
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON p.name_id = vu.planet_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
||||||
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
||||||
LEFT OUTER JOIN emp.planets ep ON ep.planet_id = p.name_id
|
LEFT OUTER JOIN emp.planets ep ON ep.planet_id = p.name_id
|
||||||
|
@ -43,8 +47,7 @@ BEGIN
|
||||||
LEFT OUTER JOIN users.vacations v ON v.account_id = en.owner_id
|
LEFT OUTER JOIN users.vacations v ON v.account_id = en.owner_id
|
||||||
LEFT OUTER JOIN battles.battles b
|
LEFT OUTER JOIN battles.battles b
|
||||||
ON b.location_id = p.name_id AND b.last_tick IS NULL
|
ON b.location_id = p.name_id AND b.last_tick IS NULL
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_FLEET_ARRIVALS'
|
|
||||||
AND f.status = 'AVAILABLE' AND fm.time_left = 1
|
AND f.status = 'AVAILABLE' AND fm.time_left = 1
|
||||||
GROUP BY p.name_id , ep.empire_id , f.owner_id , v.status , b.id
|
GROUP BY p.name_id , ep.empire_id , f.owner_id , v.status , b.id
|
||||||
LOOP
|
LOOP
|
||||||
|
@ -103,16 +106,17 @@ BEGIN
|
||||||
SELECT f.location_id , ln.name , f.owner_id , fon.name ,
|
SELECT f.location_id , ln.name , f.owner_id , fon.name ,
|
||||||
f.name , fs.power , f.attacking , fm.source_id , sn.name
|
f.name , fs.power , f.attacking , fm.source_id , sn.name
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON p.name_id = vu.planet_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
||||||
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
||||||
INNER JOIN fleets.stats_view fs ON fs.id = f.id
|
INNER JOIN fleets.stats_view fs ON fs.id = f.id
|
||||||
INNER JOIN naming.empire_names fon ON fon.id = f.owner_id
|
INNER JOIN naming.empire_names fon ON fon.id = f.owner_id
|
||||||
INNER JOIN naming.map_names ln ON ln.id = f.location_id
|
INNER JOIN naming.map_names ln ON ln.id = f.location_id
|
||||||
INNER JOIN naming.map_names sn ON sn.id = fm.source_id
|
INNER JOIN naming.map_names sn ON sn.id = fm.source_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_FLEET_ARRIVALS'
|
|
||||||
AND f.status = 'AVAILABLE' AND fm.time_left = 1;
|
AND f.status = 'AVAILABLE' AND fm.time_left = 1;
|
||||||
|
|
||||||
-- Delete movement records, set redeployment penalties, update battles
|
-- Delete movement records, set redeployment penalties, update battles
|
||||||
|
@ -120,15 +124,16 @@ BEGIN
|
||||||
f.attacking AS attacking , b.id AS battle ,
|
f.attacking AS attacking , b.id AS battle ,
|
||||||
f.location_id AS location
|
f.location_id AS location
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON p.name_id = vu.planet_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
||||||
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
||||||
INNER JOIN fleets.stats_view fs ON fs.id = f.id
|
INNER JOIN fleets.stats_view fs ON fs.id = f.id
|
||||||
LEFT OUTER JOIN battles.battles b
|
LEFT OUTER JOIN battles.battles b
|
||||||
ON b.location_id = p.name_id AND b.last_tick IS NULL
|
ON b.location_id = p.name_id AND b.last_tick IS NULL
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_FLEET_ARRIVALS'
|
|
||||||
AND f.status = 'AVAILABLE' AND fm.time_left = 1
|
AND f.status = 'AVAILABLE' AND fm.time_left = 1
|
||||||
LOOP
|
LOOP
|
||||||
DELETE FROM fleets.movements
|
DELETE FROM fleets.movements
|
||||||
|
@ -146,3 +151,9 @@ BEGIN
|
||||||
PERFORM events.commit_fleet_arrivals( c_tick );
|
PERFORM events.commit_fleet_arrivals( c_tick );
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'FleetArrivals' ,
|
||||||
|
'Fleets which were one update away are arriving at their destinations.' ,
|
||||||
|
'process_fleet_arrivals_updates'
|
||||||
|
);
|
|
@ -7,7 +7,7 @@
|
||||||
-- --------------------------------------------------------
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION sys.process_planet_fleet_movements_updates( c_tick BIGINT )
|
CREATE OR REPLACE FUNCTION sys.process_fleet_movements_updates( c_tick BIGINT )
|
||||||
RETURNS VOID
|
RETURNS VOID
|
||||||
STRICT VOLATILE
|
STRICT VOLATILE
|
||||||
SECURITY INVOKER
|
SECURITY INVOKER
|
||||||
|
@ -18,13 +18,14 @@ DECLARE
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Lock all records
|
-- Lock all records
|
||||||
PERFORM f.id FROM sys.updates su
|
PERFORM f.id FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON p.name_id = vu.planet_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
||||||
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
||||||
INNER JOIN emp.empires e ON e.name_id = f.owner_id
|
INNER JOIN emp.empires e ON e.name_id = f.owner_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_FLEET_MOVEMENTS'
|
|
||||||
AND f.status = 'AVAILABLE'
|
AND f.status = 'AVAILABLE'
|
||||||
FOR UPDATE;
|
FOR UPDATE;
|
||||||
|
|
||||||
|
@ -36,8 +37,10 @@ BEGIN
|
||||||
rp.system_id AS is_ref_point_system , rp.orbit AS is_ref_point_orbit ,
|
rp.system_id AS is_ref_point_system , rp.orbit AS is_ref_point_orbit ,
|
||||||
rps.x AS is_ref_point_x , rps.y AS is_ref_point_y
|
rps.x AS is_ref_point_x , rps.y AS is_ref_point_y
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON p.name_id = vu.planet_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN verse.systems s ON s.id = p.system_id
|
INNER JOIN verse.systems s ON s.id = p.system_id
|
||||||
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
||||||
INNER JOIN fleets.ships fs ON fs.fleet_id = f.id
|
INNER JOIN fleets.ships fs ON fs.fleet_id = f.id
|
||||||
|
@ -46,8 +49,7 @@ BEGIN
|
||||||
LEFT OUTER JOIN fleets.ms_system isms ON isms.movement_id = f.id
|
LEFT OUTER JOIN fleets.ms_system isms ON isms.movement_id = f.id
|
||||||
LEFT OUTER JOIN verse.planets rp ON isms.ref_point_id = rp.name_id
|
LEFT OUTER JOIN verse.planets rp ON isms.ref_point_id = rp.name_id
|
||||||
LEFT OUTER JOIN verse.systems rps ON rps.id = rp.system_id
|
LEFT OUTER JOIN verse.systems rps ON rps.id = rp.system_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_FLEET_MOVEMENTS'
|
|
||||||
AND f.status = 'AVAILABLE' AND m.state_time_left = 1
|
AND f.status = 'AVAILABLE' AND m.state_time_left = 1
|
||||||
GROUP BY f.id , s.x , s.y , s.id , isms.ref_point_id , isms.outwards ,
|
GROUP BY f.id , s.x , s.y , s.id , isms.ref_point_id , isms.outwards ,
|
||||||
isms.past_ref_point , rp.system_id , rp.orbit , rps.x , rps.y
|
isms.past_ref_point , rp.system_id , rp.orbit , rps.x , rps.y
|
||||||
|
@ -98,13 +100,19 @@ BEGIN
|
||||||
time_left = time_left - 1
|
time_left = time_left - 1
|
||||||
WHERE fleet_id IN (
|
WHERE fleet_id IN (
|
||||||
SELECT f.id FROM sys.updates su
|
SELECT f.id FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON p.name_id = vu.planet_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
||||||
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
INNER JOIN fleets.movements fm ON fm.fleet_id = f.id
|
||||||
INNER JOIN emp.empires e ON e.name_id = f.owner_id
|
INNER JOIN emp.empires e ON e.name_id = f.owner_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_FLEET_MOVEMENTS'
|
|
||||||
AND f.status = 'AVAILABLE' );
|
AND f.status = 'AVAILABLE' );
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'FleetMovements' ,
|
||||||
|
'Fleets are moving.' ,
|
||||||
|
'process_fleet_movements_updates'
|
||||||
|
);
|
|
@ -7,7 +7,7 @@
|
||||||
-- --------------------------------------------------------
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION sys.process_planet_fleet_status_updates( c_tick BIGINT )
|
CREATE OR REPLACE FUNCTION sys.process_fleet_status_updates( c_tick BIGINT )
|
||||||
RETURNS VOID
|
RETURNS VOID
|
||||||
STRICT VOLATILE
|
STRICT VOLATILE
|
||||||
SECURITY INVOKER
|
SECURITY INVOKER
|
||||||
|
@ -18,26 +18,27 @@ DECLARE
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Lock all records
|
-- Lock all records
|
||||||
PERFORM f.id FROM sys.updates su
|
PERFORM f.id FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON p.name_id = vu.planet_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
INNER JOIN fleets.fleets f ON f.location_id = p.name_id
|
||||||
INNER JOIN emp.empires e ON e.name_id = f.owner_id
|
INNER JOIN emp.empires e ON e.name_id = f.owner_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_FLEET_STATUS'
|
|
||||||
FOR UPDATE;
|
FOR UPDATE;
|
||||||
|
|
||||||
-- Fleet deployments
|
-- Fleet deployments
|
||||||
FOR rec IN SELECT f.id AS fleet , f.owner_id AS owner , f.location_id AS location ,
|
FOR rec IN SELECT f.id AS fleet , f.owner_id AS owner , f.location_id AS location ,
|
||||||
b.id AS battle
|
b.id AS battle
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN fleets.fleets f ON f.location_id = vu.planet_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN fleets.fleets f ON f.location_id = vu.name_id
|
||||||
AND f.status = 'DEPLOYING' AND f.penalty = 1
|
AND f.status = 'DEPLOYING' AND f.penalty = 1
|
||||||
INNER JOIN emp.empires e ON e.name_id = f.owner_id
|
INNER JOIN emp.empires e ON e.name_id = f.owner_id
|
||||||
LEFT OUTER JOIN battles.battles b
|
LEFT OUTER JOIN battles.battles b
|
||||||
ON b.location_id = f.location_id AND b.last_tick IS NULL
|
ON b.location_id = f.location_id AND b.last_tick IS NULL
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_FLEET_STATUS'
|
|
||||||
LOOP
|
LOOP
|
||||||
-- Add fleet to battle (will be ignored if battle is NULL)
|
-- Add fleet to battle (will be ignored if battle is NULL)
|
||||||
PERFORM battles.add_fleet( rec.battle , rec.fleet , TRUE , c_tick );
|
PERFORM battles.add_fleet( rec.battle , rec.fleet , TRUE , c_tick );
|
||||||
|
@ -62,16 +63,21 @@ BEGIN
|
||||||
|
|
||||||
-- Fleets that must become available
|
-- Fleets that must become available
|
||||||
UPDATE fleets.fleets f SET status = 'AVAILABLE' , penalty = 0
|
UPDATE fleets.fleets f SET status = 'AVAILABLE' , penalty = 0
|
||||||
FROM sys.updates su , verse.updates vu
|
FROM sys.updates su , verse.planets_updates vu
|
||||||
WHERE vu.update_id = su.id AND f.location_id = vu.planet_id
|
WHERE vu.update_id = su.update_id AND f.location_id = vu.name_id
|
||||||
AND f.penalty = 1 AND su.status = 'PROCESSING'
|
AND f.penalty = 1
|
||||||
AND su.gu_type = 'PLANET_FLEET_STATUS' AND su.last_tick = c_tick;
|
AND su.update_state = 'PROCESSING' AND su.update_last = c_tick;
|
||||||
|
|
||||||
-- Fleets that still have a penalty
|
-- Fleets that still have a penalty
|
||||||
UPDATE fleets.fleets f SET penalty = penalty - 1
|
UPDATE fleets.fleets f SET penalty = penalty - 1
|
||||||
FROM sys.updates su , verse.updates vu
|
FROM sys.updates su , verse.planets_updates vu
|
||||||
WHERE vu.update_id = su.id AND f.location_id = vu.planet_id
|
WHERE vu.update_id = su.update_id AND f.location_id = vu.name_id
|
||||||
AND f.penalty > 1 AND su.status = 'PROCESSING'
|
AND f.penalty > 1
|
||||||
AND su.gu_type = 'PLANET_FLEET_STATUS' AND su.last_tick = c_tick;
|
AND su.update_state = 'PROCESSING' AND su.update_last = c_tick;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'FleetStatus' ,
|
||||||
|
'Fleet states (e.g. "deploying", "unavailable", etc...) are being updated.' ,
|
||||||
|
'process_fleet_status_updates'
|
||||||
|
);
|
|
@ -7,7 +7,7 @@
|
||||||
-- --------------------------------------------------------
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION sys.process_planet_battle_start_updates( c_tick BIGINT )
|
CREATE OR REPLACE FUNCTION sys.process_battle_start_updates( c_tick BIGINT )
|
||||||
RETURNS VOID
|
RETURNS VOID
|
||||||
STRICT VOLATILE
|
STRICT VOLATILE
|
||||||
SECURITY INVOKER
|
SECURITY INVOKER
|
||||||
|
@ -17,12 +17,14 @@ DECLARE
|
||||||
BEGIN
|
BEGIN
|
||||||
FOR p_id IN SELECT p.name_id
|
FOR p_id IN SELECT p.name_id
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON vu.planet_id = p.name_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
LEFT OUTER JOIN battles.battles b
|
LEFT OUTER JOIN battles.battles b
|
||||||
ON b.location_id = p.name_id AND b.last_tick IS NULL
|
ON b.location_id = p.name_id AND b.last_tick IS NULL
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_BATTLE_START' AND b.location_id IS NULL
|
AND b.location_id IS NULL
|
||||||
FOR UPDATE OF p
|
FOR UPDATE OF p
|
||||||
LOOP
|
LOOP
|
||||||
IF battles.check_start( p_id ) THEN
|
IF battles.check_start( p_id ) THEN
|
||||||
|
@ -32,10 +34,13 @@ BEGIN
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'BattleStart' ,
|
||||||
|
'Battles are being started.' ,
|
||||||
|
'process_battle_start_updates'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION sys.process_battle_main_updates( c_tick BIGINT )
|
||||||
CREATE OR REPLACE FUNCTION sys.process_planet_battle_main_updates( c_tick BIGINT )
|
|
||||||
RETURNS VOID
|
RETURNS VOID
|
||||||
STRICT VOLATILE
|
STRICT VOLATILE
|
||||||
SECURITY INVOKER
|
SECURITY INVOKER
|
||||||
|
@ -63,13 +68,14 @@ BEGIN
|
||||||
FOR rec IN SELECT b.id AS battle , b.first_tick AS first_tick ,
|
FOR rec IN SELECT b.id AS battle , b.first_tick AS first_tick ,
|
||||||
b.location_id AS location , ( ph.current / p.population )::REAL AS happiness
|
b.location_id AS location , ( ph.current / p.population )::REAL AS happiness
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON vu.planet_id = p.name_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN verse.planet_happiness ph ON ph.planet_id = p.name_id
|
INNER JOIN verse.planet_happiness ph ON ph.planet_id = p.name_id
|
||||||
INNER JOIN battles.battles b
|
INNER JOIN battles.battles b
|
||||||
ON b.location_id = p.name_id AND b.last_tick IS NULL
|
ON b.location_id = p.name_id AND b.last_tick IS NULL
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_BATTLE_MAIN'
|
|
||||||
FOR UPDATE OF p , b
|
FOR UPDATE OF p , b
|
||||||
LOOP
|
LOOP
|
||||||
PERFORM sys.write_log( 'BattleUpdate' , 'DEBUG'::log_level , 'Handling battle #' || rec.battle
|
PERFORM sys.write_log( 'BattleUpdate' , 'DEBUG'::log_level , 'Handling battle #' || rec.battle
|
||||||
|
@ -115,10 +121,14 @@ BEGIN
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'BattleCompute' ,
|
||||||
|
'In-progress battles are being updated.' ,
|
||||||
|
'process_battle_main_updates'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION sys.process_planet_battle_end_updates( c_tick BIGINT )
|
CREATE OR REPLACE FUNCTION sys.process_battle_end_updates( c_tick BIGINT )
|
||||||
RETURNS VOID
|
RETURNS VOID
|
||||||
STRICT VOLATILE
|
STRICT VOLATILE
|
||||||
SECURITY INVOKER
|
SECURITY INVOKER
|
||||||
|
@ -129,12 +139,13 @@ DECLARE
|
||||||
BEGIN
|
BEGIN
|
||||||
FOR rec IN SELECT b.id AS battle , b.location_id AS location
|
FOR rec IN SELECT b.id AS battle , b.location_id AS location
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON vu.planet_id = p.name_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN battles.battles b
|
INNER JOIN battles.battles b
|
||||||
ON b.location_id = p.name_id AND b.last_tick IS NULL
|
ON b.location_id = p.name_id AND b.last_tick IS NULL
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_BATTLE_END'
|
|
||||||
FOR UPDATE OF p , b
|
FOR UPDATE OF p , b
|
||||||
LOOP
|
LOOP
|
||||||
IF battles.get_fleets_power( rec.battle , c_tick , TRUE ) = 0 THEN
|
IF battles.get_fleets_power( rec.battle , c_tick , TRUE ) = 0 THEN
|
||||||
|
@ -179,3 +190,7 @@ BEGIN
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'BattleEnd' ,
|
||||||
|
'Finished battles are being ended.' ,
|
||||||
|
'process_battle_end_updates'
|
||||||
|
);
|
|
@ -16,24 +16,31 @@ DECLARE
|
||||||
p_id INT;
|
p_id INT;
|
||||||
BEGIN
|
BEGIN
|
||||||
-- Lock all records
|
-- Lock all records
|
||||||
PERFORM p.name_id FROM sys.updates su
|
PERFORM p.name_id
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
FROM sys.updates su
|
||||||
INNER JOIN verse.planets p ON p.name_id = vu.planet_id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN emp.planets ep ON p.name_id = vu.planet_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
INNER JOIN emp.empires e ON e.name_id = ep.empire_id
|
INNER JOIN verse.planets p
|
||||||
INNER JOIN emp.abandon a ON a.planet_id = p.name_id
|
USING ( name_id )
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
INNER JOIN emp.planets ep
|
||||||
AND su.gu_type = 'PLANET_ABANDON'
|
ON ep.planet_id = p.name_id
|
||||||
|
INNER JOIN emp.empires e
|
||||||
|
ON e.name_id = ep.empire_id
|
||||||
|
INNER JOIN emp.abandon a
|
||||||
|
ON a.planet_id = p.name_id
|
||||||
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
FOR UPDATE;
|
FOR UPDATE;
|
||||||
|
|
||||||
-- Handle planets where time has run out
|
-- Handle planets where time has run out
|
||||||
FOR p_id IN SELECT p.name_id
|
FOR p_id IN SELECT p.name_id
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON p.name_id = vu.planet_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN emp.abandon a ON a.planet_id = p.name_id
|
INNER JOIN emp.abandon a ON a.planet_id = p.name_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_ABANDON' AND a.time_left = 1
|
AND a.time_left = 1
|
||||||
LOOP
|
LOOP
|
||||||
PERFORM emp.leave_planet( p_id );
|
PERFORM emp.leave_planet( p_id );
|
||||||
END LOOP;
|
END LOOP;
|
||||||
|
@ -41,8 +48,14 @@ BEGIN
|
||||||
-- Update all abandon records
|
-- Update all abandon records
|
||||||
UPDATE emp.abandon a SET time_left = a.time_left - 1
|
UPDATE emp.abandon a SET time_left = a.time_left - 1
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
AND su.gu_type = 'PLANET_ABANDON' AND a.planet_id = vu.planet_id;
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
|
AND a.planet_id = vu.name_id;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'PlanetAbandon' ,
|
||||||
|
'Planets are being abandoned.' ,
|
||||||
|
'process_planet_abandon_updates'
|
||||||
|
);
|
|
@ -46,8 +46,10 @@ BEGIN
|
||||||
qi.destroy AS destroy , qi.building_id AS building ,
|
qi.destroy AS destroy , qi.building_id AS building ,
|
||||||
b.work AS req_work , b.cost AS req_cost
|
b.work AS req_work , b.cost AS req_cost
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON vu.planet_id = p.name_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN emp.planets ep ON ep.planet_id = p.name_id
|
INNER JOIN emp.planets ep ON ep.planet_id = p.name_id
|
||||||
INNER JOIN emp.empires e ON e.name_id = ep.empire_id
|
INNER JOIN emp.empires e ON e.name_id = ep.empire_id
|
||||||
INNER JOIN verse.planet_happiness ph ON ph.planet_id = p.name_id
|
INNER JOIN verse.planet_happiness ph ON ph.planet_id = p.name_id
|
||||||
|
@ -56,8 +58,7 @@ BEGIN
|
||||||
INNER JOIN tech.buildables b ON b.name_id = qi.building_id
|
INNER JOIN tech.buildables b ON b.name_id = qi.building_id
|
||||||
INNER JOIN naming.empire_names en ON en.id = e.name_id
|
INNER JOIN naming.empire_names en ON en.id = e.name_id
|
||||||
LEFT OUTER JOIN users.vacations v ON v.account_id = en.owner_id
|
LEFT OUTER JOIN users.vacations v ON v.account_id = en.owner_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_CONSTRUCTION'
|
|
||||||
AND ( v.account_id IS NULL OR v.status <> 'PROCESSED' )
|
AND ( v.account_id IS NULL OR v.status <> 'PROCESSED' )
|
||||||
ORDER BY e.name_id , p.name_id , qi.queue_order
|
ORDER BY e.name_id , p.name_id , qi.queue_order
|
||||||
FOR UPDATE OF p , e , q , qi
|
FOR UPDATE OF p , e , q , qi
|
||||||
|
@ -215,3 +216,8 @@ BEGIN
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'CivilianConstruction' ,
|
||||||
|
'Civilian build queues are being processed.' ,
|
||||||
|
'process_planet_construction_updates'
|
||||||
|
);
|
|
@ -44,8 +44,10 @@ BEGIN
|
||||||
qi.queue_order AS qorder , qi.amount AS amount ,
|
qi.queue_order AS qorder , qi.amount AS amount ,
|
||||||
qi.ship_id AS ship , s.work AS req_work , s.cost AS req_cost
|
qi.ship_id AS ship , s.work AS req_work , s.cost AS req_cost
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON vu.planet_id = p.name_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN emp.planets ep ON ep.planet_id = p.name_id
|
INNER JOIN emp.planets ep ON ep.planet_id = p.name_id
|
||||||
INNER JOIN emp.empires e ON e.name_id = ep.empire_id
|
INNER JOIN emp.empires e ON e.name_id = ep.empire_id
|
||||||
INNER JOIN verse.planet_happiness ph ON ph.planet_id = p.name_id
|
INNER JOIN verse.planet_happiness ph ON ph.planet_id = p.name_id
|
||||||
|
@ -54,8 +56,7 @@ BEGIN
|
||||||
INNER JOIN tech.buildables s ON s.name_id = qi.ship_id
|
INNER JOIN tech.buildables s ON s.name_id = qi.ship_id
|
||||||
INNER JOIN naming.empire_names en ON en.id = e.name_id
|
INNER JOIN naming.empire_names en ON en.id = e.name_id
|
||||||
LEFT OUTER JOIN users.vacations v ON v.account_id = en.owner_id
|
LEFT OUTER JOIN users.vacations v ON v.account_id = en.owner_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_MILITARY'
|
|
||||||
AND ( v.account_id IS NULL OR v.status <> 'PROCESSED' )
|
AND ( v.account_id IS NULL OR v.status <> 'PROCESSED' )
|
||||||
ORDER BY e.name_id , p.name_id , qi.queue_order
|
ORDER BY e.name_id , p.name_id , qi.queue_order
|
||||||
FOR UPDATE OF p , e , q , qi
|
FOR UPDATE OF p , e , q , qi
|
||||||
|
@ -225,3 +226,8 @@ BEGIN
|
||||||
DROP TABLE blt_ships;
|
DROP TABLE blt_ships;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'MilitaryConstruction' ,
|
||||||
|
'Military build queues are being processed.' ,
|
||||||
|
'process_planet_military_updates'
|
||||||
|
);
|
|
@ -37,11 +37,12 @@ BEGIN
|
||||||
ph.target AS target , ph.current AS happy_pop ,
|
ph.target AS target , ph.current AS happy_pop ,
|
||||||
( ph.current / p.population )::REAL AS current
|
( ph.current / p.population )::REAL AS current
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON vu.planet_id = p.name_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN verse.planet_happiness ph ON ph.planet_id = p.name_id
|
INNER JOIN verse.planet_happiness ph ON ph.planet_id = p.name_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_POPULATION'
|
|
||||||
FOR UPDATE OF p, ph
|
FOR UPDATE OF p, ph
|
||||||
LOOP
|
LOOP
|
||||||
IF round( rec.target / rel_ch ) = round( rec.current / rel_ch ) THEN
|
IF round( rec.target / rel_ch ) = round( rec.current / rel_ch ) THEN
|
||||||
|
@ -102,3 +103,8 @@ BEGIN
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'PlanetPopulation' ,
|
||||||
|
'Planet populations are being updated.' ,
|
||||||
|
'process_planet_population_updates'
|
||||||
|
);
|
|
@ -16,7 +16,7 @@
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* _tick The identifier of the game update
|
* _tick The identifier of the game update
|
||||||
*/
|
*/
|
||||||
CREATE OR REPLACE FUNCTION sys.process_planet_res_regen_updates( _tick BIGINT )
|
CREATE OR REPLACE FUNCTION sys.process_res_regen_updates( _tick BIGINT )
|
||||||
RETURNS VOID
|
RETURNS VOID
|
||||||
STRICT VOLATILE
|
STRICT VOLATILE
|
||||||
SECURITY INVOKER
|
SECURITY INVOKER
|
||||||
|
@ -31,17 +31,21 @@ AS $process_planet_res_regen_updates$
|
||||||
)
|
)
|
||||||
|
|
||||||
FROM sys.updates _upd_sys
|
FROM sys.updates _upd_sys
|
||||||
INNER JOIN verse.updates _upd_verse
|
INNER JOIN verse.planets_updates _upd_verse
|
||||||
ON _upd_verse.update_id = _upd_sys.id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
|
||||||
WHERE _upd_sys.last_tick = $1
|
WHERE _upd_sys.update_last = $1
|
||||||
AND _upd_sys.status = 'PROCESSING'
|
AND _upd_sys.update_state = 'PROCESSING'
|
||||||
AND _upd_sys.gu_type = 'PLANET_RES_REGEN'
|
AND _provider.planet_id = _upd_verse.name_id;
|
||||||
AND _provider.planet_id = _upd_verse.planet_id;
|
|
||||||
|
|
||||||
$process_planet_res_regen_updates$ LANGUAGE SQL;
|
$process_planet_res_regen_updates$ LANGUAGE SQL;
|
||||||
|
|
||||||
|
|
||||||
REVOKE EXECUTE
|
REVOKE EXECUTE
|
||||||
ON FUNCTION sys.process_planet_res_regen_updates( BIGINT )
|
ON FUNCTION sys.process_res_regen_updates( BIGINT )
|
||||||
FROM PUBLIC;
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'ResourceRegeneration' ,
|
||||||
|
'Resource providers are being regenerated.' ,
|
||||||
|
'process_res_regen_updates'
|
||||||
|
);
|
|
@ -20,13 +20,14 @@ BEGIN
|
||||||
( ph.current / p.population )::REAL AS happiness ,
|
( ph.current / p.population )::REAL AS happiness ,
|
||||||
( ea.planet_id IS NULL ) AS produces_income
|
( ea.planet_id IS NULL ) AS produces_income
|
||||||
FROM sys.updates su
|
FROM sys.updates su
|
||||||
INNER JOIN verse.updates vu ON vu.update_id = su.id
|
INNER JOIN verse.planets_updates vu
|
||||||
INNER JOIN verse.planets p ON vu.planet_id = p.name_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
|
INNER JOIN verse.planets p
|
||||||
|
USING ( name_id )
|
||||||
INNER JOIN verse.planet_happiness ph ON ph.planet_id = p.name_id
|
INNER JOIN verse.planet_happiness ph ON ph.planet_id = p.name_id
|
||||||
INNER JOIN verse.planet_money pm ON pm.planet_id = p.name_id
|
INNER JOIN verse.planet_money pm ON pm.planet_id = p.name_id
|
||||||
LEFT OUTER JOIN emp.abandon ea ON ea.planet_id = p.name_id
|
LEFT OUTER JOIN emp.abandon ea ON ea.planet_id = p.name_id
|
||||||
WHERE su.last_tick = c_tick AND su.status = 'PROCESSING'
|
WHERE su.update_last = c_tick AND su.update_state = 'PROCESSING'
|
||||||
AND su.gu_type = 'PLANET_MONEY'
|
|
||||||
FOR UPDATE OF p, pm
|
FOR UPDATE OF p, pm
|
||||||
LOOP
|
LOOP
|
||||||
IF rec.produces_income THEN
|
IF rec.produces_income THEN
|
||||||
|
@ -43,3 +44,8 @@ BEGIN
|
||||||
END LOOP;
|
END LOOP;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'PlanetMoney' ,
|
||||||
|
'Planets'' income and upkeep are being updated.' ,
|
||||||
|
'process_planet_money_updates'
|
||||||
|
);
|
|
@ -127,12 +127,12 @@ AS $gu_pmc_get_data$
|
||||||
pmc_total AS total_weight
|
pmc_total AS total_weight
|
||||||
|
|
||||||
FROM sys.updates _upd_sys
|
FROM sys.updates _upd_sys
|
||||||
INNER JOIN verse.updates _upd_verse
|
INNER JOIN verse.planets_updates _upd_verse
|
||||||
ON _upd_sys.id = _upd_verse.update_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
INNER JOIN verse.planets _planet
|
INNER JOIN verse.planets _planet
|
||||||
ON _planet.name_id = _upd_verse.planet_id
|
USING ( name_id )
|
||||||
INNER JOIN verse.resource_providers _resprov
|
INNER JOIN verse.resource_providers _resprov
|
||||||
USING ( planet_id )
|
ON planet_id = name_id
|
||||||
INNER JOIN verse.planet_resources _pres
|
INNER JOIN verse.planet_resources _pres
|
||||||
USING ( planet_id , resource_name_id )
|
USING ( planet_id , resource_name_id )
|
||||||
LEFT OUTER JOIN (
|
LEFT OUTER JOIN (
|
||||||
|
@ -141,10 +141,10 @@ AS $gu_pmc_get_data$
|
||||||
pmc_total , _happ.current AS happiness
|
pmc_total , _happ.current AS happiness
|
||||||
|
|
||||||
FROM sys.updates _upd_sys
|
FROM sys.updates _upd_sys
|
||||||
INNER JOIN verse.updates _upd_verse
|
INNER JOIN verse.planets_updates _upd_verse
|
||||||
ON _upd_sys.id = _upd_verse.update_id
|
USING ( update_id , updtype_id , updtgt_id )
|
||||||
INNER JOIN emp.planets _emp_planet
|
INNER JOIN emp.planets _emp_planet
|
||||||
USING ( planet_id )
|
ON _emp_planet.planet_id = _upd_verse.name_id
|
||||||
INNER JOIN emp.empires _emp
|
INNER JOIN emp.empires _emp
|
||||||
ON _emp_planet.empire_id = _emp.name_id
|
ON _emp_planet.empire_id = _emp.name_id
|
||||||
INNER JOIN emp.mining_settings _emset
|
INNER JOIN emp.mining_settings _emset
|
||||||
|
@ -161,17 +161,15 @@ AS $gu_pmc_get_data$
|
||||||
) AS _pmset
|
) AS _pmset
|
||||||
USING ( empire_id , planet_id , resource_name_id )
|
USING ( empire_id , planet_id , resource_name_id )
|
||||||
|
|
||||||
WHERE _upd_sys.last_tick = $1
|
WHERE _upd_sys.update_last = $1
|
||||||
AND _upd_sys.status = 'PROCESSING'
|
AND _upd_sys.update_state = 'PROCESSING'
|
||||||
AND _upd_sys.gu_type = 'PLANET_MINING'
|
|
||||||
|
|
||||||
FOR SHARE OF _emp_planet , _emp , _emset , _happ
|
FOR SHARE OF _emp_planet , _emp , _emset , _happ
|
||||||
) AS _owner
|
) AS _owner
|
||||||
USING ( planet_id , resource_name_id )
|
USING ( planet_id , resource_name_id )
|
||||||
|
|
||||||
WHERE _upd_sys.last_tick = $1
|
WHERE _upd_sys.update_last = $1
|
||||||
AND _upd_sys.status = 'PROCESSING'
|
AND _upd_sys.update_state = 'PROCESSING'
|
||||||
AND _upd_sys.gu_type = 'PLANET_MINING'
|
|
||||||
|
|
||||||
FOR UPDATE OF _resprov , _pres
|
FOR UPDATE OF _resprov , _pres
|
||||||
FOR SHARE OF _planet ;
|
FOR SHARE OF _planet ;
|
||||||
|
@ -286,3 +284,8 @@ $process_planet_mining_updates$ LANGUAGE PLPGSQL;
|
||||||
REVOKE EXECUTE
|
REVOKE EXECUTE
|
||||||
ON FUNCTION sys.process_planet_mining_updates( BIGINT )
|
ON FUNCTION sys.process_planet_mining_updates( BIGINT )
|
||||||
FROM PUBLIC;
|
FROM PUBLIC;
|
||||||
|
|
||||||
|
SELECT sys.register_update_type( 'Planets' , 'PlanetMining' ,
|
||||||
|
'Resources are being extracted from mines.' ,
|
||||||
|
'process_planet_mining_updates'
|
||||||
|
);
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* Tests of the update target definitions
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
-- Make sure we get rid of all existing definitions first
|
||||||
|
DELETE FROM sys.update_types;
|
||||||
|
DELETE FROM sys.update_targets;
|
||||||
|
|
||||||
|
-- Create a test schema & table which will be used as the target
|
||||||
|
CREATE SCHEMA test
|
||||||
|
CREATE TABLE test(
|
||||||
|
test_id SERIAL NOT NULL PRIMARY KEY
|
||||||
|
)
|
||||||
|
CREATE TABLE test2(
|
||||||
|
test_id SERIAL NOT NULL PRIMARY KEY
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ***** TESTS BEGIN HERE *****
|
||||||
|
SELECT plan( 8 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update target definitions - Failure if schema/table do not exist' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ INSERT INTO sys.update_targets(
|
||||||
|
updtgt_name , updtgt_schema , updtgt_table
|
||||||
|
) VALUES (
|
||||||
|
'Test' , 'test' , 'does_not_exist'
|
||||||
|
); $$ ,
|
||||||
|
23503
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update target definitions - Successful insertion' );
|
||||||
|
SELECT lives_ok( $$
|
||||||
|
INSERT INTO sys.update_targets(
|
||||||
|
updtgt_name , updtgt_schema , updtgt_table
|
||||||
|
) VALUES (
|
||||||
|
'Test' , 'test' , 'test'
|
||||||
|
);
|
||||||
|
$$ );
|
||||||
|
SELECT diag_test_name( 'Update target definitions - Specific update table - Exists after insertion' );
|
||||||
|
SELECT has_table( 'test' , 'test_updates' , 'No specific update table' );
|
||||||
|
SELECT diag_test_name( 'Update target definitions - Specific update table - Primary key' );
|
||||||
|
SELECT index_is_primary( 'test' , 'test_updates' , 'test_updates_pkey' );
|
||||||
|
SELECT diag_test_name( 'Update target definitions - Specific update table - Primary key definition' );
|
||||||
|
SELECT has_index( 'test' , 'test_updates' , 'test_updates_pkey' ,
|
||||||
|
ARRAY[ 'updtgt_id' , 'updtype_id' , 'update_id' , 'test_id' ] );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update target definitions - Duplicate name' );
|
||||||
|
SELECT throws_ok( $$
|
||||||
|
INSERT INTO sys.update_targets(
|
||||||
|
updtgt_name , updtgt_schema , updtgt_table
|
||||||
|
) VALUES (
|
||||||
|
'Test' , 'test' , 'test2'
|
||||||
|
);
|
||||||
|
$$ , 23505 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update target definitions - Duplicate table reference' );
|
||||||
|
SELECT throws_ok( $$
|
||||||
|
INSERT INTO sys.update_targets(
|
||||||
|
updtgt_name , updtgt_schema , updtgt_table
|
||||||
|
) VALUES (
|
||||||
|
'Test 2' , 'test' , 'test'
|
||||||
|
);
|
||||||
|
$$ , 23505 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update target definitions - Deletion drops the specific update table' );
|
||||||
|
DELETE FROM sys.update_targets;
|
||||||
|
SELECT hasnt_table( 'test' , 'test_updates' , 'Specific update table still exists' );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Tests of the update type definitions
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
-- Make sure we get rid of all existing definitions first
|
||||||
|
DELETE FROM sys.update_types;
|
||||||
|
DELETE FROM sys.update_targets;
|
||||||
|
|
||||||
|
-- Create a test schema & tables which will be used as targets
|
||||||
|
CREATE SCHEMA test
|
||||||
|
CREATE TABLE test(
|
||||||
|
test_id SERIAL NOT NULL PRIMARY KEY
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Create target definitions
|
||||||
|
INSERT INTO sys.update_targets(
|
||||||
|
updtgt_id , updtgt_name , updtgt_schema , updtgt_table
|
||||||
|
) VALUES (
|
||||||
|
1 , 'Test 1' , 'test' , 'test'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Create a dummy stored procedure in the sys schema
|
||||||
|
CREATE FUNCTION sys.dummy_update_func( c_tick BIGINT )
|
||||||
|
RETURNS INT LANGUAGE SQL AS 'SELECT 1;';
|
||||||
|
|
||||||
|
-- ***** TESTS BEGIN HERE *****
|
||||||
|
SELECT plan( 14 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update types - Definition with invalid target' );
|
||||||
|
SELECT throws_ok( $$
|
||||||
|
INSERT INTO sys.update_types(
|
||||||
|
updtype_id , updtgt_id , updtype_name , updtype_description , updtype_ordering
|
||||||
|
) VALUES (
|
||||||
|
1 , 5 , 'Test' , '' , 1
|
||||||
|
);
|
||||||
|
$$ , 23503 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update types - Definition with invalid stored procedure name' );
|
||||||
|
SELECT throws_ok( $$
|
||||||
|
INSERT INTO sys.update_types(
|
||||||
|
updtype_id , updtgt_id , updtype_name , updtype_description , updtype_ordering , updtype_proc_name
|
||||||
|
) VALUES (
|
||||||
|
1 , 5 , 'Test' , '' , 1 , 'does_not_exist'
|
||||||
|
);
|
||||||
|
$$ , 'P0001' );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update types - Successful insertion with no procedure name' );
|
||||||
|
SELECT lives_ok( $$
|
||||||
|
INSERT INTO sys.update_types(
|
||||||
|
updtype_id , updtgt_id , updtype_name , updtype_description , updtype_ordering
|
||||||
|
) VALUES (
|
||||||
|
1 , 1 , 'Test' , '' , 1
|
||||||
|
);
|
||||||
|
$$ );
|
||||||
|
DELETE FROM sys.update_types;
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update types - Successful insertion with a procedure name' );
|
||||||
|
SELECT lives_ok( $$
|
||||||
|
INSERT INTO sys.update_types(
|
||||||
|
updtype_id , updtgt_id , updtype_name , updtype_description , updtype_ordering , updtype_proc_name
|
||||||
|
) VALUES (
|
||||||
|
1 , 1 , 'Test' , '' , 1 , 'dummy_update_func'
|
||||||
|
);
|
||||||
|
$$ );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update types - Setting an existing row''s stored procedure to something invalid fails' );
|
||||||
|
SELECT throws_ok( $$
|
||||||
|
UPDATE sys.update_types
|
||||||
|
SET updtype_proc_name = 'does_not_exist'
|
||||||
|
WHERE updtype_id = 1
|
||||||
|
$$ , 'P0001' );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update types - Failure on duplicate name' );
|
||||||
|
SELECT throws_ok( $$
|
||||||
|
INSERT INTO sys.update_types(
|
||||||
|
updtype_id , updtgt_id , updtype_name , updtype_description , updtype_ordering
|
||||||
|
) VALUES (
|
||||||
|
2 , 1 , 'Test' , '' , 1
|
||||||
|
);
|
||||||
|
$$ , 23505 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update types - Failure on duplicate stored procedure' );
|
||||||
|
SELECT throws_ok( $$
|
||||||
|
INSERT INTO sys.update_types(
|
||||||
|
updtype_id , updtgt_id , updtype_name , updtype_description , updtype_ordering , updtype_proc_name
|
||||||
|
) VALUES (
|
||||||
|
3 , 1 , 'Test 2' , '' , 1 , 'dummy_update_func'
|
||||||
|
);
|
||||||
|
$$ , 23505 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update types - Target deletion fails if type definitions exist' );
|
||||||
|
SELECT throws_ok( $$ DELETE FROM sys.update_targets WHERE updtgt_id = 1 $$ , 23503 );
|
||||||
|
|
||||||
|
DELETE FROM sys.update_types;
|
||||||
|
|
||||||
|
INSERT INTO sys.update_types(
|
||||||
|
updtype_id , updtgt_id , updtype_name , updtype_description , updtype_ordering
|
||||||
|
) VALUES (
|
||||||
|
1 , 1 , 'Test 1' , '' , 1
|
||||||
|
) , (
|
||||||
|
2 , 1 , 'Test 3' , '' , 75
|
||||||
|
) , (
|
||||||
|
3 , 1 , 'Test 2' , '' , 2
|
||||||
|
);
|
||||||
|
SELECT diag_test_name( 'Update types - Ordering column is updated' );
|
||||||
|
SELECT set_eq(
|
||||||
|
$$ SELECT updtype_id , updtype_ordering FROM sys.update_types $$ ,
|
||||||
|
$$ VALUES ( 1 , 2 ) , ( 3 , 4 ) , ( 2 , 6 ) $$
|
||||||
|
);
|
||||||
|
DELETE FROM sys.update_types;
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO test.test VALUES ( 1 ) , ( 2 );
|
||||||
|
INSERT INTO sys.update_types(
|
||||||
|
updtype_id , updtgt_id , updtype_name , updtype_description , updtype_ordering
|
||||||
|
) VALUES (
|
||||||
|
1 , 1 , 'Test 1' , '' , 1
|
||||||
|
);
|
||||||
|
SELECT diag_test_name( 'Update types - Type creation adds specific rows for missing items' );
|
||||||
|
SELECT set_eq(
|
||||||
|
$$ SELECT updtgt_id , updtype_id , test_id FROM test.test_updates $$ ,
|
||||||
|
$$ VALUES ( 1 , 1 , 1 ) , ( 1 , 1 , 2 ) $$
|
||||||
|
);
|
||||||
|
SELECT diag_test_name( 'Update types - Type creation adds genertic rows for missing items' );
|
||||||
|
SELECT set_eq(
|
||||||
|
$$ SELECT test_id , update_state , update_last
|
||||||
|
FROM test.test_updates
|
||||||
|
INNER JOIN sys.updates USING ( updtgt_id , updtype_id , update_id ) $$ ,
|
||||||
|
$$ VALUES ( 1 , 'FUTURE'::sys.update_state_type , -1 ) ,
|
||||||
|
( 2 , 'FUTURE'::sys.update_state_type , '-1' ) $$
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO test.test VALUES ( 3 );
|
||||||
|
SELECT diag_test_name( 'Update types - New items have automatically inserted update rows' );
|
||||||
|
SELECT set_eq(
|
||||||
|
$$ SELECT test_id , updtype_id , update_state , update_last
|
||||||
|
FROM test.test_updates
|
||||||
|
INNER JOIN sys.updates USING ( updtgt_id , updtype_id , update_id )
|
||||||
|
WHERE test_id = 3 $$ ,
|
||||||
|
$$ VALUES ( 3 , 1 , 'FUTURE'::sys.update_state_type , -1 ) $$
|
||||||
|
);
|
||||||
|
|
||||||
|
DELETE FROM sys.update_types WHERE updtype_id = 1;
|
||||||
|
SELECT diag_test_name( 'Update types - Type deletion cascades to specific rows' );
|
||||||
|
SELECT is_empty( $$ SELECT * FROM test.test_updates $$ );
|
||||||
|
SELECT diag_test_name( 'Update types - Type deletion cascades to generic rows' );
|
||||||
|
SELECT is_empty( $$ SELECT * FROM sys.updates $$ );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -16,7 +16,7 @@ BEGIN;
|
||||||
SELECT _create_emp_names( 1 , 'testEmp' );
|
SELECT _create_emp_names( 1 , 'testEmp' );
|
||||||
|
|
||||||
/***** TESTS BEGIN HERE *****/
|
/***** TESTS BEGIN HERE *****/
|
||||||
SELECT plan( 8 );
|
SELECT plan( 7 );
|
||||||
|
|
||||||
SELECT emp.create_empire( _get_emp_name( 'testEmp1' ) ,
|
SELECT emp.create_empire( _get_emp_name( 'testEmp1' ) ,
|
||||||
_get_map_name( 'testPlanet1' ) ,
|
_get_map_name( 'testPlanet1' ) ,
|
||||||
|
@ -57,16 +57,5 @@ BEGIN;
|
||||||
FROM emp.resources
|
FROM emp.resources
|
||||||
WHERE empire_id = _get_emp_name( 'testEmp1' );
|
WHERE empire_id = _get_emp_name( 'testEmp1' );
|
||||||
|
|
||||||
SELECT diag_test_name( 'emp.create_empire() - Empire update records' );
|
|
||||||
SELECT is( _eur.quantity , _utv.quantity)
|
|
||||||
FROM (
|
|
||||||
SELECT COUNT(*) AS quantity FROM emp.updates
|
|
||||||
WHERE empire_id = _get_emp_name( 'testEmp1' )
|
|
||||||
) AS _eur , (
|
|
||||||
SELECT COUNT(*) AS quantity
|
|
||||||
FROM unnest( enum_range( NULL::update_type ) ) AS _row
|
|
||||||
WHERE _row::TEXT LIKE 'EMPIRE_%'
|
|
||||||
) AS _utv;
|
|
||||||
|
|
||||||
SELECT * FROM finish( );
|
SELECT * FROM finish( );
|
||||||
ROLLBACK;
|
ROLLBACK;
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Test the sys.start_tick() function
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
-- Delete all registered update types and targets
|
||||||
|
DELETE FROM sys.update_types;
|
||||||
|
DELETE FROM sys.update_targets;
|
||||||
|
|
||||||
|
-- Create a new update target / type which will be used in tests
|
||||||
|
CREATE SCHEMA test
|
||||||
|
CREATE TABLE test( test_id INT NOT NULL PRIMARY KEY );
|
||||||
|
INSERT INTO sys.update_targets VALUES ( 1 , 'Test' , 'test' , 'test' );
|
||||||
|
INSERT INTO sys.update_types VALUES ( 1 , 1 , 'Test' , 2 , '' , NULL , NULL );
|
||||||
|
|
||||||
|
-- Insert a few update rows
|
||||||
|
INSERT INTO test.test VALUES ( 1 ) , ( 2 ) , ( 3 );
|
||||||
|
|
||||||
|
-- ***** TESTS BEGIN HERE *****
|
||||||
|
SELECT plan( 5 );
|
||||||
|
|
||||||
|
UPDATE sys.status SET maintenance_start = now( ) , current_tick = NULL;
|
||||||
|
SELECT diag_test_name( 'sys.start_tick() - Ticks do not start if maintenance mode is active' );
|
||||||
|
SELECT ok( sys.start_tick( ) IS NULL );
|
||||||
|
|
||||||
|
UPDATE sys.status SET maintenance_start = NULL , current_tick = next_tick;
|
||||||
|
SELECT diag_test_name( 'sys.start_tick() - Ticks do not start if current_tick is set' );
|
||||||
|
SELECT ok( sys.start_tick( ) IS NULL );
|
||||||
|
|
||||||
|
UPDATE sys.status SET current_tick = NULL , next_tick = 2;
|
||||||
|
UPDATE sys.updates SET update_state = 'PROCESSED' , update_last = -1;
|
||||||
|
UPDATE sys.updates su SET update_last = 514
|
||||||
|
FROM test.test_updates tu
|
||||||
|
WHERE su.update_id = tu.update_id
|
||||||
|
AND tu.test_id = 3;
|
||||||
|
SELECT diag_test_name( 'sys.start_tick() - Returns next tick identifier when ticks can start' );
|
||||||
|
SELECT is( sys.start_tick( ) , 2::BIGINT );
|
||||||
|
SELECT diag_test_name( 'sys.start_tick() - System status is updated' );
|
||||||
|
SELECT set_eq(
|
||||||
|
$$ SELECT current_tick , next_tick FROM sys.status $$ ,
|
||||||
|
$$ VALUES ( 2 , 3 ) $$
|
||||||
|
);
|
||||||
|
SELECT diag_test_name( 'sys.start_tick() - Update rows are marked for processing when necessary' );
|
||||||
|
SELECT set_eq(
|
||||||
|
$$ SELECT test_id
|
||||||
|
FROM test.test_updates
|
||||||
|
INNER JOIN sys.updates
|
||||||
|
USING ( update_id , updtgt_id , updtype_id )
|
||||||
|
WHERE update_state = 'FUTURE' AND update_last = 2 $$ ,
|
||||||
|
$$ VALUES ( 1 ) , ( 2 ) $$
|
||||||
|
);
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Tests of the sys.check_stuck_tick() function
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
-- Delete all registered update types and targets
|
||||||
|
DELETE FROM sys.update_types;
|
||||||
|
DELETE FROM sys.update_targets;
|
||||||
|
|
||||||
|
-- Create a new update target / type which will be used in tests
|
||||||
|
CREATE SCHEMA test
|
||||||
|
CREATE TABLE test( test_id INT NOT NULL PRIMARY KEY );
|
||||||
|
INSERT INTO sys.update_targets VALUES ( 1 , 'Test' , 'test' , 'test' );
|
||||||
|
INSERT INTO sys.update_types VALUES ( 1 , 1 , 'Test' , 2 , '' , NULL , NULL );
|
||||||
|
|
||||||
|
-- Insert a few update rows
|
||||||
|
INSERT INTO test.test VALUES ( 1 ) , ( 2 ) , ( 3 );
|
||||||
|
|
||||||
|
-- Replace sys.end_tick() with a function that inserts into some table
|
||||||
|
CREATE TABLE end_tick_calls( tick_id BIGINT NOT NULL );
|
||||||
|
CREATE OR REPLACE FUNCTION sys.end_tick( tick_id BIGINT )
|
||||||
|
RETURNS VOID LANGUAGE SQL
|
||||||
|
AS 'INSERT INTO end_tick_calls VALUES( $1 );';
|
||||||
|
|
||||||
|
-- ***** TESTS BEGIN HERE *****
|
||||||
|
SELECT plan( 8 );
|
||||||
|
|
||||||
|
UPDATE sys.status SET maintenance_start = NULL , current_tick = NULL;
|
||||||
|
SELECT diag_test_name( 'sys.check_stuck_tick() - Returns NULL if no tick is in progress' );
|
||||||
|
SELECT ok( sys.check_stuck_tick() IS NULL );
|
||||||
|
SELECT diag_test_name( 'sys.check_stuck_tick() - Does not call sys.end_tick() if no tick is in progress' );
|
||||||
|
SELECT is_empty( $$ SELECT * FROM end_tick_calls $$ );
|
||||||
|
DELETE FROM end_tick_calls;
|
||||||
|
|
||||||
|
UPDATE sys.status SET maintenance_start = now( ) , current_tick = 2;
|
||||||
|
SELECT diag_test_name( 'sys.check_stuck_tick() - Returns NULL if maintenance mode is enabled' );
|
||||||
|
SELECT ok( sys.check_stuck_tick() IS NULL );
|
||||||
|
SELECT diag_test_name( 'sys.check_stuck_tick() - Does not call sys.end_tick() if maintenance mode is enabled' );
|
||||||
|
SELECT is_empty( $$ SELECT * FROM end_tick_calls $$ );
|
||||||
|
DELETE FROM end_tick_calls;
|
||||||
|
|
||||||
|
UPDATE sys.status SET maintenance_start = NULL , current_tick = 2;
|
||||||
|
UPDATE sys.updates SET update_state = 'PROCESSED' , update_last = 2;
|
||||||
|
SELECT diag_test_name( 'sys.check_stuck_tick() - Returns NULL if a tick is in progress but there are no updates left' );
|
||||||
|
SELECT ok( sys.check_stuck_tick() IS NULL );
|
||||||
|
SELECT diag_test_name( 'sys.check_stuck_tick() - Calls sys.end_tick() if a tick is in progress but there are no updates left' );
|
||||||
|
SELECT set_eq(
|
||||||
|
$$ SELECT * FROM end_tick_calls $$ ,
|
||||||
|
$$ VALUES ( 2 ) $$
|
||||||
|
);
|
||||||
|
DELETE FROM end_tick_calls;
|
||||||
|
|
||||||
|
UPDATE sys.status SET maintenance_start = NULL , current_tick = 2;
|
||||||
|
UPDATE sys.updates SET update_state = 'FUTURE' , update_last = 2;
|
||||||
|
SELECT diag_test_name( 'sys.check_stuck_tick() - Returns tick identifier if a tick is in progress and there are updates left' );
|
||||||
|
SELECT is( sys.check_stuck_tick() , 2::BIGINT );
|
||||||
|
SELECT diag_test_name( 'sys.check_stuck_tick() - Does not call sys.end_tick() if a tick is in progress and there are updates left' );
|
||||||
|
SELECT is_empty( $$ SELECT * FROM end_tick_calls $$ );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
* Tests for the sys.process_updates() function
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
-- Delete all registered update types and targets
|
||||||
|
DELETE FROM sys.update_types;
|
||||||
|
DELETE FROM sys.update_targets;
|
||||||
|
|
||||||
|
-- Create a new update target / type which will be used in tests
|
||||||
|
CREATE SCHEMA test
|
||||||
|
CREATE TABLE test( test_id INT NOT NULL PRIMARY KEY );
|
||||||
|
INSERT INTO sys.update_targets VALUES ( 1 , 'Test' , 'test' , 'test' );
|
||||||
|
INSERT INTO sys.update_types VALUES ( 1 , 1 , 'Test 1' , 2 , '' , NULL , NULL );
|
||||||
|
INSERT INTO sys.update_types VALUES ( 2 , 1 , 'Test 2' , 4 , '' , NULL , NULL );
|
||||||
|
|
||||||
|
-- Replace sys.end_tick() with a function that inserts into some table
|
||||||
|
CREATE TABLE end_tick_calls( tick_id BIGINT NOT NULL );
|
||||||
|
CREATE OR REPLACE FUNCTION sys.end_tick( tick_id BIGINT )
|
||||||
|
RETURNS VOID LANGUAGE SQL
|
||||||
|
AS 'INSERT INTO end_tick_calls VALUES( $1 );';
|
||||||
|
|
||||||
|
-- Create a sys.process_dummy_update() function that inserts into some other table
|
||||||
|
CREATE TABLE process_update_calls( tick_id BIGINT NOT NULL );
|
||||||
|
CREATE OR REPLACE FUNCTION sys.process_dummy_update( tick_id BIGINT )
|
||||||
|
RETURNS VOID LANGUAGE SQL
|
||||||
|
AS 'INSERT INTO process_update_calls VALUES( $1 );';
|
||||||
|
|
||||||
|
-- Set the general batch size to 1
|
||||||
|
SELECT sys.uoc_constant( 'game.batchSize' , '(test)' , 'Game updates' , 1 );
|
||||||
|
|
||||||
|
-- Insert a few entries in the test table
|
||||||
|
INSERT INTO test.test
|
||||||
|
SELECT * FROM generate_series( 1 , 10 );
|
||||||
|
|
||||||
|
-- ***** TESTS BEGIN HERE *****
|
||||||
|
SELECT plan( 21 );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Case:
|
||||||
|
* Updates have not been processed yet,
|
||||||
|
*
|
||||||
|
* Expected results:
|
||||||
|
* 1) New updates of the first type have been marked for processing,
|
||||||
|
* 2) The quantity of updates marked for processing corresponds to game.batchSize
|
||||||
|
*/
|
||||||
|
UPDATE sys.updates
|
||||||
|
SET update_state = 'FUTURE' , update_last = 1;
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - General batch size - Return value' );
|
||||||
|
SELECT ok( sys.process_updates( 1 ) = ROW( TRUE , 'Test 1'::TEXT ) );
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - General batch size - Update type' );
|
||||||
|
SELECT set_eq(
|
||||||
|
$$ SELECT DISTINCT updtype_id FROM sys.updates WHERE update_state = 'PROCESSING'; $$ ,
|
||||||
|
$$ VALUES ( 1 ) $$
|
||||||
|
);
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - General batch size - Update count' );
|
||||||
|
SELECT is( COUNT(*)::INT , 1 ) FROM sys.updates WHERE update_state = 'PROCESSING';
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - General batch size - sys.end_tick() not called' );
|
||||||
|
SELECT is_empty( $$ SELECT * FROM end_tick_calls $$ );
|
||||||
|
DELETE FROM end_tick_calls;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Case:
|
||||||
|
* Updates have not been processed yet,
|
||||||
|
* Update type has a specific batch size,
|
||||||
|
*
|
||||||
|
* Expected results:
|
||||||
|
* 1) New updates have been marked for processing,
|
||||||
|
* 2) The quantity of updates marked for processing corresponds to the specific batch size
|
||||||
|
* 3) sys.end_tick() has not been called
|
||||||
|
*/
|
||||||
|
UPDATE sys.update_types SET updtype_batch_size = 2 WHERE updtype_id = 1;
|
||||||
|
UPDATE sys.updates SET update_state = 'FUTURE' , update_last = 1;
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - Specific batch size - Return value' );
|
||||||
|
SELECT ok( sys.process_updates( 1 ) = ROW( TRUE , 'Test 1'::TEXT ) );
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - Specific batch size - Update type' );
|
||||||
|
SELECT set_eq(
|
||||||
|
$$ SELECT DISTINCT updtype_id FROM sys.updates WHERE update_state = 'PROCESSING'; $$ ,
|
||||||
|
$$ VALUES ( 1 ) $$
|
||||||
|
);
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - Specific batch size - Update count' );
|
||||||
|
SELECT is( COUNT(*)::INT , 2 ) FROM sys.updates WHERE update_state = 'PROCESSING';
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - Specific batch size - sys.end_tick() not called' );
|
||||||
|
SELECT is_empty( $$ SELECT * FROM end_tick_calls $$ );
|
||||||
|
DELETE FROM end_tick_calls;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Case:
|
||||||
|
* Some but not all updates of the first type have been processed,
|
||||||
|
* Batch size is greater than the quantity of updates left to process for the type.
|
||||||
|
*
|
||||||
|
* Expected results:
|
||||||
|
* 1) All updates of the first type are marked either for processing or as processed.
|
||||||
|
* 2) sys.end_tick() has not been called
|
||||||
|
*/
|
||||||
|
UPDATE sys.update_types SET updtype_batch_size = 2 WHERE updtype_id = 1;
|
||||||
|
UPDATE sys.updates SET update_state = 'FUTURE' , update_last = 1;
|
||||||
|
UPDATE sys.updates
|
||||||
|
SET update_state = 'PROCESSED'
|
||||||
|
WHERE update_id IN (
|
||||||
|
SELECT update_id FROM sys.updates WHERE updtype_id = 1 LIMIT 9
|
||||||
|
);
|
||||||
|
UPDATE sys.update_types SET updtype_batch_size = 2 WHERE updtype_id = 1;
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - End of type updates - Return value' );
|
||||||
|
SELECT ok( sys.process_updates( 1 ) = ROW( TRUE , 'Test 1'::TEXT ) );
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - End of type updates - Update type' );
|
||||||
|
SELECT set_eq(
|
||||||
|
$$ SELECT DISTINCT updtype_id FROM sys.updates WHERE update_state <> 'FUTURE'; $$ ,
|
||||||
|
$$ VALUES ( 1 ) $$
|
||||||
|
);
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - End of type updates - Update count' );
|
||||||
|
SELECT is( COUNT(*)::INT , 10 ) FROM sys.updates WHERE update_state <> 'FUTURE';
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - End of type updates - sys.end_tick() not called' );
|
||||||
|
SELECT is_empty( $$ SELECT * FROM end_tick_calls $$ );
|
||||||
|
DELETE FROM end_tick_calls;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Case:
|
||||||
|
* All updates of the first type have been processed.
|
||||||
|
* No updates of the second type have been processed.
|
||||||
|
*
|
||||||
|
* Expected results:
|
||||||
|
* 1) Some updates from the second type have been marked for processing.
|
||||||
|
* 2) sys.end_tick() has not been called
|
||||||
|
*/
|
||||||
|
UPDATE sys.updates SET update_state = 'FUTURE' , update_last = 1;
|
||||||
|
UPDATE sys.updates
|
||||||
|
SET update_state = 'PROCESSED'
|
||||||
|
WHERE updtype_id = 1;
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - Update type transition - Return value' );
|
||||||
|
SELECT ok( sys.process_updates( 1 ) = ROW( TRUE , 'Test 2'::TEXT ) );
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - Update type transition - Update type' );
|
||||||
|
SELECT set_eq(
|
||||||
|
$$ SELECT DISTINCT updtype_id FROM sys.updates WHERE update_state = 'PROCESSING'; $$ ,
|
||||||
|
$$ VALUES ( 2 ) $$
|
||||||
|
);
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - Update type transition - Update count' );
|
||||||
|
SELECT is( COUNT(*)::INT , 1 ) FROM sys.updates WHERE update_state = 'PROCESSING';
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - Update type transition - sys.end_tick() not called' );
|
||||||
|
SELECT is_empty( $$ SELECT * FROM end_tick_calls $$ );
|
||||||
|
DELETE FROM end_tick_calls;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Case:
|
||||||
|
* There no updates to process
|
||||||
|
* Updates are marked as 'PROCESSING'
|
||||||
|
*
|
||||||
|
* Expected results:
|
||||||
|
* 1) _has_more return value is FALSE,
|
||||||
|
* 2) All updates are marked as 'PROCESSED'
|
||||||
|
* 3) sys.end_tick() has been called.
|
||||||
|
*/
|
||||||
|
UPDATE sys.updates SET update_state = 'PROCESSING' , update_last = 1;
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - No updates left - Return value' );
|
||||||
|
SELECT ok( sys.process_updates( 1 ) = ROW( FALSE , NULL::TEXT ) );
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - No updates left - All updates processed' );
|
||||||
|
SELECT is_empty( $$ SELECT * FROM sys.updates WHERE update_state <> 'PROCESSED' $$ );
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - No updates left - sys.end_tick() called' );
|
||||||
|
SELECT set_eq( $$ SELECT * FROM end_tick_calls $$ , $$ VALUES ( 1 ) $$ );
|
||||||
|
DELETE FROM end_tick_calls;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Case:
|
||||||
|
* The update type has an in-base processing function
|
||||||
|
*
|
||||||
|
* Expected result:
|
||||||
|
* 1) _process_externally return value is NULL
|
||||||
|
* 2) The processing function has been called.
|
||||||
|
*/
|
||||||
|
UPDATE sys.updates SET update_state = 'FUTURE' , update_last = 1;
|
||||||
|
UPDATE sys.update_types
|
||||||
|
SET updtype_batch_size = NULL , updtype_proc_name = 'process_dummy_update'
|
||||||
|
WHERE updtype_id = 1;
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - In-base processing - Return value' );
|
||||||
|
SELECT ok( sys.process_updates( 1 ) = ROW( TRUE , NULL::TEXT ) );
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - In-base processing - Processing function called' );
|
||||||
|
SELECT set_eq( $$ SELECT * FROM process_update_calls $$ , $$ VALUES ( 1 ) $$ );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,17 @@
|
||||||
|
/*
|
||||||
|
* Check that core update targets have been defined correctly
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 3 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update targets - Definitions present' );
|
||||||
|
SELECT is( COUNT(*)::INT , 2 ) FROM sys.update_targets;
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update targets - verse.planets_updates exists' );
|
||||||
|
SELECT has_table( 'verse' , 'planets_updates' , 'Missing planets update table' );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'Update targets - emp.empires_updates exists' );
|
||||||
|
SELECT has_table( 'emp' , 'empires_updates' , 'Missing empires update table' );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -2,6 +2,12 @@
|
||||||
* Test the sys.process_empire_resources_updates() function
|
* Test the sys.process_empire_resources_updates() function
|
||||||
*/
|
*/
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
/* Start by deleting all update types except for the one
|
||||||
|
* we're interested in.
|
||||||
|
*/
|
||||||
|
DELETE FROM sys.update_types
|
||||||
|
WHERE updtype_name <> 'EmpireResources';
|
||||||
|
|
||||||
/* We need:
|
/* We need:
|
||||||
* - three types of resources,
|
* - three types of resources,
|
||||||
* - two empire with resources records, and the corresponding update
|
* - two empire with resources records, and the corresponding update
|
||||||
|
@ -35,14 +41,15 @@ BEGIN;
|
||||||
|
|
||||||
/* Make sure update 0 is set to be processed for testEmp1 */
|
/* Make sure update 0 is set to be processed for testEmp1 */
|
||||||
UPDATE sys.updates
|
UPDATE sys.updates
|
||||||
SET status = 'PROCESSING' , last_tick = 0;
|
SET update_state = 'PROCESSING' , update_last = 0;
|
||||||
|
|
||||||
/* Create testEmp2 empire, make sure it will not be processed */
|
/* Create testEmp2 empire, make sure it will not be processed */
|
||||||
SELECT emp.create_empire( _get_emp_name( 'testEmp2' ) , _get_map_name( 'testPlanet2' ) , 1 );
|
SELECT emp.create_empire( _get_emp_name( 'testEmp2' ) , _get_map_name( 'testPlanet2' ) , 1 );
|
||||||
UPDATE sys.updates _su
|
UPDATE sys.updates _su
|
||||||
SET status = 'PROCESSED' , last_tick = 0
|
SET update_state = 'PROCESSED' , update_last = 0
|
||||||
FROM emp.updates _eu
|
FROM emp.empires_updates _eu
|
||||||
WHERE _eu.update_id = _su.id AND _eu.empire_id = _get_emp_name( 'testEmp2' );
|
WHERE _eu.update_id = _su.update_id
|
||||||
|
AND _eu.name_id = _get_emp_name( 'testEmp2' );
|
||||||
|
|
||||||
/****** TESTS BEGIN HERE ******/
|
/****** TESTS BEGIN HERE ******/
|
||||||
SELECT plan( 19 );
|
SELECT plan( 19 );
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
* Test for the sys.process_planet_res_regen_updates() function
|
* Test for the sys.process_planet_res_regen_updates() function
|
||||||
*/
|
*/
|
||||||
BEGIN;
|
BEGIN;
|
||||||
|
/* Start by deleting all update types except for the one
|
||||||
|
* we're interested in.
|
||||||
|
*/
|
||||||
|
DELETE FROM sys.update_types
|
||||||
|
WHERE updtype_name <> 'ResourceRegeneration';
|
||||||
|
|
||||||
/* We need to create a natural resource and a pair of planets */
|
/* We need to create a natural resource and a pair of planets */
|
||||||
\i utils/strings.sql
|
\i utils/strings.sql
|
||||||
|
@ -34,23 +39,19 @@ BEGIN;
|
||||||
0.5 , 0.5
|
0.5 , 0.5
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Insert update records for the planets. Planets 1 and 2 will be processed,
|
/* Modify update records for the planets. Planets 1 and 2 will be processed,
|
||||||
* planet testPlanet3 will not.
|
* planet testPlanet3 will not.
|
||||||
*/
|
*/
|
||||||
INSERT INTO sys.updates ( id , gu_type , status , last_tick )
|
UPDATE sys.updates su
|
||||||
VALUES ( 1 , 'PLANET_RES_REGEN' , 'PROCESSING' , 0 );
|
SET update_state = 'PROCESSING' , update_last = 0
|
||||||
INSERT INTO verse.updates ( update_id , planet_id )
|
FROM verse.planets_updates vu
|
||||||
VALUES ( 1 , _get_map_name( 'testPlanet1' ) );
|
WHERE vu.update_id = su.update_id
|
||||||
|
AND vu.name_id <> _get_map_name( 'testPlanet3' );
|
||||||
INSERT INTO sys.updates ( id , gu_type , status , last_tick )
|
UPDATE sys.updates su
|
||||||
VALUES ( 2 , 'PLANET_RES_REGEN' , 'PROCESSING' , 0 );
|
SET update_state = 'PROCESSED' , update_last = 0
|
||||||
INSERT INTO verse.updates ( update_id , planet_id )
|
FROM verse.planets_updates vu
|
||||||
VALUES ( 2 , _get_map_name( 'testPlanet2' ) );
|
WHERE vu.update_id = su.update_id
|
||||||
|
AND vu.name_id = _get_map_name( 'testPlanet3' );
|
||||||
INSERT INTO sys.updates ( id , gu_type , status , last_tick )
|
|
||||||
VALUES ( 3 , 'PLANET_RES_REGEN' , 'PROCESSED' , 0 );
|
|
||||||
INSERT INTO verse.updates ( update_id , planet_id )
|
|
||||||
VALUES ( 3 , _get_map_name( 'testPlanet3' ) );
|
|
||||||
|
|
||||||
/* Helper function that gets a provider's current quantity */
|
/* Helper function that gets a provider's current quantity */
|
||||||
CREATE FUNCTION _get_quantity( _planet TEXT )
|
CREATE FUNCTION _get_quantity( _planet TEXT )
|
||||||
|
@ -65,7 +66,7 @@ BEGIN;
|
||||||
SELECT plan( 6 );
|
SELECT plan( 6 );
|
||||||
|
|
||||||
|
|
||||||
SELECT sys.process_planet_res_regen_updates( 10 );
|
SELECT sys.process_res_regen_updates( 10 );
|
||||||
SELECT diag_test_name( 'PLANET_RES_REGEN update - Wrong update identifier (1/3)' );
|
SELECT diag_test_name( 'PLANET_RES_REGEN update - Wrong update identifier (1/3)' );
|
||||||
SELECT ok( _get_quantity( 'testPlanet1' ) = 50 );
|
SELECT ok( _get_quantity( 'testPlanet1' ) = 50 );
|
||||||
SELECT diag_test_name( 'PLANET_RES_REGEN update - Wrong update identifier (2/3)' );
|
SELECT diag_test_name( 'PLANET_RES_REGEN update - Wrong update identifier (2/3)' );
|
||||||
|
@ -74,7 +75,7 @@ BEGIN;
|
||||||
SELECT ok( _get_quantity( 'testPlanet3' ) = 50 );
|
SELECT ok( _get_quantity( 'testPlanet3' ) = 50 );
|
||||||
|
|
||||||
|
|
||||||
SELECT sys.process_planet_res_regen_updates( 0 );
|
SELECT sys.process_res_regen_updates( 0 );
|
||||||
SELECT diag_test_name( 'PLANET_RES_REGEN update - Processed planet with quantity < max.' );
|
SELECT diag_test_name( 'PLANET_RES_REGEN update - Processed planet with quantity < max.' );
|
||||||
SELECT ok( _get_quantity( 'testPlanet1' ) > 50 );
|
SELECT ok( _get_quantity( 'testPlanet1' ) > 50 );
|
||||||
SELECT diag_test_name( 'PLANET_RES_REGEN update - Processed planet with quantity = max.' );
|
SELECT diag_test_name( 'PLANET_RES_REGEN update - Processed planet with quantity = max.' );
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.update_targets
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 4 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.update_targets - No INSERT privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ INSERT INTO sys.update_targets DEFAULT VALUES; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.update_targets - No UPDATE privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ UPDATE sys.update_targets SET updtgt_id = 42; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.update_targets - No SELECT privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ SELECT * FROM sys.update_targets; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.update_targets - No DELETE privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ DELETE FROM sys.update_targets; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.update_types
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 4 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.update_types - No INSERT privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ INSERT INTO sys.update_types DEFAULT VALUES; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.update_types - No UPDATE privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ UPDATE sys.update_types SET updtgt_id = 42; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.update_types - No SELECT privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ SELECT * FROM sys.update_types; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.update_types - No DELETE privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ DELETE FROM sys.update_types; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.updates
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 4 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.updates - No INSERT privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ INSERT INTO sys.updates DEFAULT VALUES; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.updates - No UPDATE privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ UPDATE sys.updates SET updtgt_id = 42; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.updates - No SELECT privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ SELECT * FROM sys.updates; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.updates - No DELETE privilege' );
|
||||||
|
SELECT throws_ok(
|
||||||
|
$$ DELETE FROM sys.updates; $$ ,
|
||||||
|
42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.get_table_pkey()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.get_table_pkey() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.get_table_pkey( 'a' , 'b' ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.tgf_speupd_before_insert()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.tgf_speupd_before_insert() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.tgf_speupd_before_insert( ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.tgf_speupd_after_delete()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.tgf_speupd_after_delete() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.tgf_speupd_after_delete( ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.insert_missing_updates()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.insert_missing_updates() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.insert_missing_updates( 1 ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.tgf_insert_missing_updates()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.tgf_insert_missing_updates() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.tgf_insert_missing_updates( ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.tgf_updtype_after_insert_row()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.tgf_updtype_after_insert_row() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.tgf_updtype_after_insert_row( ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.tgf_check_update_type_proc()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.tgf_check_update_type_proc() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.tgf_check_update_type_proc( ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.tgf_reorder_update_types()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.tgf_reorder_update_types() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.tgf_reorder_update_types( ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.tgf_updtgt_before_insert()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.tgf_updtgt_before_insert() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.tgf_updtgt_before_insert( ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.tgf_updtgt_after_insert()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.tgf_updtgt_after_insert() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.tgf_updtgt_after_insert( ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.tgf_updtgt_after_delete()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.tgf_updtgt_after_delete() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.tgf_updtgt_after_delete( ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.register_update_type()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.register_update_type() - No EXECUTE privilege' );
|
||||||
|
SELECT throws_ok( $$ SELECT sys.register_update_type( '' , '' , '' , 'x' ) $$ , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.start_tick()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.start_tick() - EXECUTE privilege' );
|
||||||
|
SELECT lives_ok( 'SELECT sys.start_tick( )' );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.check_stuck_tick()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.check_stuck_tick() - EXECUTE privilege' );
|
||||||
|
SELECT lives_ok( 'SELECT sys.check_stuck_tick( )' );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.process_updates()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.process_updates() - EXECUTE privilege' );
|
||||||
|
SELECT lives_ok( 'SELECT sys.process_updates( 1 )' );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -1,11 +0,0 @@
|
||||||
/*
|
|
||||||
* Test privileges on sys.process_planet_res_regen_updates()
|
|
||||||
*/
|
|
||||||
BEGIN;
|
|
||||||
SELECT plan( 1 );
|
|
||||||
|
|
||||||
SELECT diag_test_name( 'sys.process_planet_res_regen_updates() - Privileges' );
|
|
||||||
SELECT throws_ok( 'SELECT sys.process_planet_res_regen_updates( 1 )' , 42501 );
|
|
||||||
|
|
||||||
SELECT * FROM finish( );
|
|
||||||
ROLLBACK;
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
/*
|
||||||
|
* Test privileges on sys.process_res_regen_updates()
|
||||||
|
*/
|
||||||
|
BEGIN;
|
||||||
|
SELECT plan( 1 );
|
||||||
|
|
||||||
|
SELECT diag_test_name( 'sys.process_res_regen_updates() - Privileges' );
|
||||||
|
SELECT throws_ok( 'SELECT sys.process_res_regen_updates( 1 )' , 42501 );
|
||||||
|
|
||||||
|
SELECT * FROM finish( );
|
||||||
|
ROLLBACK;
|
|
@ -21,13 +21,13 @@
|
||||||
* - Finally, we need a planet that matches the criterias but isn't
|
* - Finally, we need a planet that matches the criterias but isn't
|
||||||
* scheduled for an update at the time.
|
* scheduled for an update at the time.
|
||||||
*
|
*
|
||||||
* FIXME: for now, locking is NOT tested. I simply have no idea how to do
|
|
||||||
* it, at least not without creating an extra database and that
|
|
||||||
* kind of stuff.
|
|
||||||
*
|
|
||||||
* FIXME: cannot test where the happiness column comes from until values
|
* FIXME: cannot test where the happiness column comes from until values
|
||||||
* all use DOUBLE PRECISION.
|
* all use DOUBLE PRECISION.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
DELETE FROM sys.update_types
|
||||||
|
WHERE updtype_name <> 'PlanetMining';
|
||||||
|
|
||||||
\i utils/strings.sql
|
\i utils/strings.sql
|
||||||
\i utils/resources.sql
|
\i utils/resources.sql
|
||||||
\i utils/accounts.sql
|
\i utils/accounts.sql
|
||||||
|
@ -41,17 +41,7 @@ SELECT _create_emp_names( 4 , 'empire' );
|
||||||
INSERT INTO emp.empires ( name_id , cash )
|
INSERT INTO emp.empires ( name_id , cash )
|
||||||
SELECT id , 0 FROM naming.empire_names;
|
SELECT id , 0 FROM naming.empire_names;
|
||||||
|
|
||||||
/* Planet #1 */
|
|
||||||
INSERT INTO sys.updates( id , gu_type , status , last_tick )
|
|
||||||
VALUES ( 1 , 'PLANET_MINING' , 'PROCESSING' , 0 );
|
|
||||||
INSERT INTO verse.updates( update_id , planet_id )
|
|
||||||
VALUES ( 1 , _get_map_name( 'planet1' ) );
|
|
||||||
|
|
||||||
/* Planet #2 */
|
/* Planet #2 */
|
||||||
INSERT INTO sys.updates( id , gu_type , status , last_tick )
|
|
||||||
VALUES ( 2 , 'PLANET_MINING' , 'PROCESSING' , 0 );
|
|
||||||
INSERT INTO verse.updates( update_id , planet_id )
|
|
||||||
VALUES ( 2 , _get_map_name( 'planet2' ) );
|
|
||||||
INSERT INTO verse.resource_providers (
|
INSERT INTO verse.resource_providers (
|
||||||
planet_id , resource_name_id , resprov_quantity_max ,
|
planet_id , resource_name_id , resprov_quantity_max ,
|
||||||
resprov_quantity , resprov_difficulty , resprov_recovery
|
resprov_quantity , resprov_difficulty , resprov_recovery
|
||||||
|
@ -64,10 +54,6 @@ INSERT INTO verse.resource_providers (
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Planet #3 */
|
/* Planet #3 */
|
||||||
INSERT INTO sys.updates( id , gu_type , status , last_tick )
|
|
||||||
VALUES ( 3 , 'PLANET_MINING' , 'PROCESSING' , 0 );
|
|
||||||
INSERT INTO verse.updates( update_id , planet_id )
|
|
||||||
VALUES ( 3 , _get_map_name( 'planet3' ) );
|
|
||||||
INSERT INTO verse.resource_providers (
|
INSERT INTO verse.resource_providers (
|
||||||
planet_id , resource_name_id , resprov_quantity_max ,
|
planet_id , resource_name_id , resprov_quantity_max ,
|
||||||
resprov_quantity , resprov_difficulty , resprov_recovery
|
resprov_quantity , resprov_difficulty , resprov_recovery
|
||||||
|
@ -90,10 +76,6 @@ INSERT INTO emp.mining_settings( empire_id , resource_name_id , empmset_weight )
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Planet #4 */
|
/* Planet #4 */
|
||||||
INSERT INTO sys.updates( id , gu_type , status , last_tick )
|
|
||||||
VALUES ( 4 , 'PLANET_MINING' , 'PROCESSING' , 0 );
|
|
||||||
INSERT INTO verse.updates( update_id , planet_id )
|
|
||||||
VALUES ( 4 , _get_map_name( 'planet4' ) );
|
|
||||||
INSERT INTO verse.resource_providers (
|
INSERT INTO verse.resource_providers (
|
||||||
planet_id , resource_name_id , resprov_quantity_max ,
|
planet_id , resource_name_id , resprov_quantity_max ,
|
||||||
resprov_quantity , resprov_difficulty , resprov_recovery
|
resprov_quantity , resprov_difficulty , resprov_recovery
|
||||||
|
@ -123,10 +105,6 @@ INSERT INTO emp.planet_mining_settings(
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Planet #5 */
|
/* Planet #5 */
|
||||||
INSERT INTO sys.updates( id , gu_type , status , last_tick )
|
|
||||||
VALUES ( 5 , 'PLANET_MINING' , 'PROCESSING' , 0 );
|
|
||||||
INSERT INTO verse.updates( update_id , planet_id )
|
|
||||||
VALUES ( 5 , _get_map_name( 'planet5' ) );
|
|
||||||
INSERT INTO verse.planet_happiness ( planet_id , current , target )
|
INSERT INTO verse.planet_happiness ( planet_id , current , target )
|
||||||
VALUES ( _get_map_name( 'planet5' ) , 0.5 , 0.5 );
|
VALUES ( _get_map_name( 'planet5' ) , 0.5 , 0.5 );
|
||||||
INSERT INTO emp.planets ( empire_id , planet_id )
|
INSERT INTO emp.planets ( empire_id , planet_id )
|
||||||
|
@ -139,10 +117,6 @@ INSERT INTO emp.mining_settings( empire_id , resource_name_id , empmset_weight )
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Planet #6 */
|
/* Planet #6 */
|
||||||
INSERT INTO sys.updates( id , gu_type , status , last_tick )
|
|
||||||
VALUES ( 6 , 'PLANET_MINING' , 'PROCESSED' , 0 );
|
|
||||||
INSERT INTO verse.updates( update_id , planet_id )
|
|
||||||
VALUES ( 6 , _get_map_name( 'planet6' ) );
|
|
||||||
INSERT INTO verse.resource_providers (
|
INSERT INTO verse.resource_providers (
|
||||||
planet_id , resource_name_id , resprov_quantity_max ,
|
planet_id , resource_name_id , resprov_quantity_max ,
|
||||||
resprov_quantity , resprov_difficulty , resprov_recovery
|
resprov_quantity , resprov_difficulty , resprov_recovery
|
||||||
|
@ -168,3 +142,16 @@ INSERT INTO emp.mining_settings( empire_id , resource_name_id , empmset_weight )
|
||||||
INSERT INTO verse.planet_resources ( planet_id , resource_name_id , pres_income , pres_upkeep )
|
INSERT INTO verse.planet_resources ( planet_id , resource_name_id , pres_income , pres_upkeep )
|
||||||
SELECT p.name_id , r.resource_name_id , 42 , 0
|
SELECT p.name_id , r.resource_name_id , 42 , 0
|
||||||
FROM verse.planets p CROSS JOIN defs.resources r;
|
FROM verse.planets p CROSS JOIN defs.resources r;
|
||||||
|
|
||||||
|
/* Modify update records for the planets. Planet 6 is not to be processed.
|
||||||
|
*/
|
||||||
|
UPDATE sys.updates su
|
||||||
|
SET update_state = 'PROCESSING' , update_last = 0
|
||||||
|
FROM verse.planets_updates vu
|
||||||
|
WHERE vu.update_id = su.update_id
|
||||||
|
AND vu.name_id <> _get_map_name( 'planet6' );
|
||||||
|
UPDATE sys.updates su
|
||||||
|
SET update_state = 'PROCESSED' , update_last = 0
|
||||||
|
FROM verse.planets_updates vu
|
||||||
|
WHERE vu.update_id = su.update_id
|
||||||
|
AND vu.name_id = _get_map_name( 'planet6' );
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package com.deepclone.lw.sqld.sys;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a game update
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This class is used to transmit the results of the game updates processor. The results may
|
||||||
|
* indicate:
|
||||||
|
* <ul>
|
||||||
|
* <li>a finished game update cycle that needs no further processing,
|
||||||
|
* <li>an in-progress update cycle for which further in-base processing is needed,
|
||||||
|
* <li>an in-progress update cycle where some processing must be done in the server.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
public class GameUpdateResult
|
||||||
|
{
|
||||||
|
|
||||||
|
/** Whether the update cycle is finished */
|
||||||
|
private final boolean finished;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the type of processing that needs to be run on the server, or <code>null</code> if
|
||||||
|
* the update is finished or was processed in-base
|
||||||
|
*/
|
||||||
|
private final String localExecution;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise a "finished" game update result
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This constructor will set {@link #finished} to <code>true</code> and {@link #localExecution}
|
||||||
|
* to <code>null</code>.
|
||||||
|
*/
|
||||||
|
public GameUpdateResult( )
|
||||||
|
{
|
||||||
|
this.finished = true;
|
||||||
|
this.localExecution = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise an "incomplete" game update result
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This constructor will set {@link #finished} to <code>false</code> and {@link #localExecution}
|
||||||
|
* to the parameter's value.
|
||||||
|
*
|
||||||
|
* @param localExecution
|
||||||
|
* the name of the type of processing to be executed on the server, or
|
||||||
|
* <code>null</code> if the update was processed in-base.
|
||||||
|
*/
|
||||||
|
public GameUpdateResult( String localExecution )
|
||||||
|
{
|
||||||
|
this.finished = false;
|
||||||
|
this.localExecution = localExecution;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return <code>true</code> if the update cycle was completed or <code>false</code> if further
|
||||||
|
* processing is required.
|
||||||
|
*/
|
||||||
|
public boolean isFinished( )
|
||||||
|
{
|
||||||
|
return this.finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Name of the type of processing that needs to be run on the server, or
|
||||||
|
* <code>null</code> if the update is finished or was processed in-base
|
||||||
|
*/
|
||||||
|
public String getLocalExecution( )
|
||||||
|
{
|
||||||
|
return this.localExecution;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
package com.deepclone.lw.interfaces.game;
|
|
||||||
|
|
||||||
|
|
||||||
public interface UpdatesDAO
|
|
||||||
{
|
|
||||||
public boolean processUpdates( long tickId );
|
|
||||||
}
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.deepclone.lw.interfaces.game.updates;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game update processor
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The game update processor allows game update cycles to be executed. It also provides the ability
|
||||||
|
* to terminated an existing update cycle.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The game update processor includes a locking mechanism which can be used to prevent two
|
||||||
|
* components (for example the game update ticker task and the administration interface) from
|
||||||
|
* executing game updates simultaneously.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
public interface GameUpdateProcessor
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try locking the game update processor
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Test and set the processor's lock. This lock is not re-entrant and should only be called once
|
||||||
|
* per locking thread.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if the lock was acquired, <code>false</code> it some other
|
||||||
|
* component had already locked the processor.
|
||||||
|
*/
|
||||||
|
public boolean tryLock( );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlock the game update processor
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Reset the processor's lock. This method should be called from the thread which actually
|
||||||
|
* locked the processor in the first place, although that will not be checked.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException
|
||||||
|
* if the processor is not locked.
|
||||||
|
*/
|
||||||
|
public void unlock( )
|
||||||
|
throws IllegalStateException;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End the previous update cycle
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Check if an update cycle had already started. If such is the case, execute all remaining
|
||||||
|
* update batches.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> if an update cycle was processed or if the system is under
|
||||||
|
* maintenance, <code>false</code> otherwise.
|
||||||
|
*/
|
||||||
|
public boolean endPreviousCycle( );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a full update cycle
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Check for an update cycle that was already started. If one exists, finish it. Otherwise,
|
||||||
|
* start a new cycle and execute all batches in separate transactions.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Update cycles will not be processed if the system is under maintenance. In addition, if the
|
||||||
|
* system enters maintenance mode while updates are being processed, the processing will stop
|
||||||
|
* after the current batch.
|
||||||
|
*/
|
||||||
|
public void executeUpdateCycle( );
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package com.deepclone.lw.interfaces.game.updates;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update batch processor interface
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This interface must be implemented by all components which provide support for server-side game
|
||||||
|
* updates processing. The components will be gathered automatically when the server initialises.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface UpdateBatchProcessor
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtain the component's supported update type
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method must be implemented to return a string which corresponds to the name of the
|
||||||
|
* update type in the database's <code>sys.update_types</code> registry table.
|
||||||
|
*
|
||||||
|
* @return the update type supported by the component
|
||||||
|
*/
|
||||||
|
public String getUpdateType( );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a batch of updates
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This method is called by the main update processor in the transaction during which items from
|
||||||
|
* the batch will have been marked for processing. It should process the update, but it must not
|
||||||
|
* modify the update rows in the <code>sys.updates</code> table.
|
||||||
|
*
|
||||||
|
* @param tickId
|
||||||
|
* the identifier of the current game update cycle
|
||||||
|
*/
|
||||||
|
public void processBatch( long tickId );
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.deepclone.lw.interfaces.game.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import com.deepclone.lw.sqld.sys.GameUpdateResult;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Game updates data access
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This interface allows access to the data and stored procedure which constitute the game update
|
||||||
|
* sub-system.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
public interface UpdatesDAO
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a batch of game updates
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Execute a batch of game updates for the current game update cycle. The value returned by this
|
||||||
|
* method determines whether the caller should call the method again, and if so whether it needs
|
||||||
|
* to do some in-server processing.
|
||||||
|
*
|
||||||
|
* @param tickId
|
||||||
|
* the identifier of the current game update cycle
|
||||||
|
*
|
||||||
|
* @return the results of the game update processor
|
||||||
|
*/
|
||||||
|
public GameUpdateResult processUpdates( long tickId );
|
||||||
|
}
|
|
@ -50,6 +50,10 @@
|
||||||
<artifactId>legacyworlds-server-beans-system</artifactId>
|
<artifactId>legacyworlds-server-beans-system</artifactId>
|
||||||
<groupId>com.deepclone.lw</groupId>
|
<groupId>com.deepclone.lw</groupId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<artifactId>legacyworlds-server-beans-updates</artifactId>
|
||||||
|
<groupId>com.deepclone.lw</groupId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<artifactId>legacyworlds-server-beans-user</artifactId>
|
<artifactId>legacyworlds-server-beans-user</artifactId>
|
||||||
<groupId>com.deepclone.lw</groupId>
|
<groupId>com.deepclone.lw</groupId>
|
||||||
|
|
|
@ -24,4 +24,10 @@
|
||||||
<import resource="configuration/system-beans.xml" />
|
<import resource="configuration/system-beans.xml" />
|
||||||
<import resource="configuration/user-beans.xml" />
|
<import resource="configuration/user-beans.xml" />
|
||||||
|
|
||||||
|
<!-- Load both the "final" component configuration file and the "simple"
|
||||||
|
implementation from B6M1. -->
|
||||||
|
<import resource="configuration/game.xml" />
|
||||||
|
<import resource="configuration/simple-beans.xml" />
|
||||||
|
|
||||||
|
|
||||||
</beans>
|
</beans>
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.UpdateBatchProcessor;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dummy batch processor used in game update tests
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
class DummyBatchProcessor
|
||||||
|
implements UpdateBatchProcessor
|
||||||
|
{
|
||||||
|
/** The name that will be returned by {@link #getUpdateType()} */
|
||||||
|
private final String updateType;
|
||||||
|
|
||||||
|
/** Ticks for which the {@link #processBatch(long)} was called */
|
||||||
|
private final HashSet< Long > ticks = new HashSet< Long >( );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the update type
|
||||||
|
*
|
||||||
|
* @param updateType
|
||||||
|
* the update type
|
||||||
|
*/
|
||||||
|
public DummyBatchProcessor( String updateType )
|
||||||
|
{
|
||||||
|
this.updateType = updateType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whatever type name was set when the instance was created
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getUpdateType( )
|
||||||
|
{
|
||||||
|
return this.updateType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Adds the specified tick identifier to the set of ticks */
|
||||||
|
@Override
|
||||||
|
public void processBatch( long tickId )
|
||||||
|
{
|
||||||
|
this.ticks.add( tickId );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @return the set of ticks for which {@link #processBatch(long)} was called */
|
||||||
|
Collection< Long > getTicks( )
|
||||||
|
{
|
||||||
|
return this.ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.GameUpdateProcessor;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock game update processor
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This mock component keeps track of which of its methods have been called. It can also simulate
|
||||||
|
* runtime errors during tick processing.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MockGameUpdateProcessor
|
||||||
|
implements GameUpdateProcessor
|
||||||
|
{
|
||||||
|
private boolean tryLockCalled = false;
|
||||||
|
private boolean unlockCalled = false;
|
||||||
|
private boolean endCycleCalled = false;
|
||||||
|
private boolean executeCalled = false;
|
||||||
|
|
||||||
|
private boolean failExecute = false;
|
||||||
|
|
||||||
|
|
||||||
|
public boolean wasTryLockCalled( )
|
||||||
|
{
|
||||||
|
return this.tryLockCalled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean wasUnlockCalled( )
|
||||||
|
{
|
||||||
|
return this.unlockCalled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean wasEndCycleCalled( )
|
||||||
|
{
|
||||||
|
return this.endCycleCalled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean wasExecuteCalled( )
|
||||||
|
{
|
||||||
|
return this.executeCalled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setFailExecute( boolean failExecute )
|
||||||
|
{
|
||||||
|
this.failExecute = failExecute;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tryLock( )
|
||||||
|
{
|
||||||
|
this.tryLockCalled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unlock( )
|
||||||
|
{
|
||||||
|
this.unlockCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean endPreviousCycle( )
|
||||||
|
{
|
||||||
|
this.endCycleCalled = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void executeUpdateCycle( )
|
||||||
|
{
|
||||||
|
this.executeCalled = true;
|
||||||
|
if ( this.failExecute ) {
|
||||||
|
throw new RuntimeException( "fail" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.UpdateBatchProcessor;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock processor registry
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
class MockRegistry
|
||||||
|
implements ServerProcessorRegistry
|
||||||
|
{
|
||||||
|
|
||||||
|
/** The map of "processors" to return */
|
||||||
|
private final HashMap< String , UpdateBatchProcessor > processors = new HashMap< String , UpdateBatchProcessor >( );
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a processor to the registry
|
||||||
|
*
|
||||||
|
* @param processor
|
||||||
|
* the processor to add
|
||||||
|
*/
|
||||||
|
public void put( UpdateBatchProcessor processor )
|
||||||
|
{
|
||||||
|
this.processors.put( processor.getUpdateType( ) , processor );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UpdateBatchProcessor getProcessorFor( String type )
|
||||||
|
{
|
||||||
|
return this.processors.get( type );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.UpdatesDAO;
|
||||||
|
import com.deepclone.lw.sqld.sys.GameUpdateResult;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mock updates DAO used in tests
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
class MockUpdatesDAO
|
||||||
|
implements UpdatesDAO
|
||||||
|
{
|
||||||
|
/** The index to read at the next call to {@link #processUpdates(long)} */
|
||||||
|
public int index = 0;
|
||||||
|
|
||||||
|
/** The values to return as the update type to process */
|
||||||
|
public String[] values = new String[] { };
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GameUpdateResult processUpdates( long tickId )
|
||||||
|
{
|
||||||
|
if ( this.index >= this.values.length ) {
|
||||||
|
return new GameUpdateResult( );
|
||||||
|
}
|
||||||
|
return new GameUpdateResult( this.values[ this.index++ ] );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,240 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.game.updates.UpdateBatchProcessor;
|
||||||
|
import com.deepclone.lw.interfaces.sys.MaintenanceData;
|
||||||
|
import com.deepclone.lw.testing.MockLogger;
|
||||||
|
import com.deepclone.lw.testing.MockSystemStatus;
|
||||||
|
import com.deepclone.lw.testing.MockTransactionManager;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link GameUpdateProcessorBean}
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
public class TestGameUpdateProcessorBean
|
||||||
|
{
|
||||||
|
|
||||||
|
/** The mock logger */
|
||||||
|
private MockLogger logger;
|
||||||
|
|
||||||
|
/** Mock updates access interface */
|
||||||
|
private MockUpdatesDAO updatesDAO;
|
||||||
|
|
||||||
|
/** Mock processor registry */
|
||||||
|
private MockRegistry registry;
|
||||||
|
|
||||||
|
/** Mock system status component */
|
||||||
|
private MockSystemStatus system;
|
||||||
|
|
||||||
|
/** The instance under test */
|
||||||
|
private GameUpdateProcessorBean gup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fake batch processor which actually enables maintenance mode.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
private static class MaintenanceEnabler
|
||||||
|
implements UpdateBatchProcessor
|
||||||
|
{
|
||||||
|
|
||||||
|
/** Mock system status component */
|
||||||
|
private MockSystemStatus system;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the mock system status component
|
||||||
|
*
|
||||||
|
* @param system
|
||||||
|
* the mock system status component
|
||||||
|
*/
|
||||||
|
MaintenanceEnabler( MockSystemStatus system )
|
||||||
|
{
|
||||||
|
this.system = system;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUpdateType( )
|
||||||
|
{
|
||||||
|
return "maintenance";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void processBatch( long tickId )
|
||||||
|
{
|
||||||
|
this.system.setMaintenance( new MaintenanceData( new Timestamp( new Date( ).getTime( ) ) , new Timestamp(
|
||||||
|
new Date( ).getTime( ) ) , "no reason" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp( )
|
||||||
|
{
|
||||||
|
this.logger = new MockLogger( );
|
||||||
|
this.updatesDAO = new MockUpdatesDAO( );
|
||||||
|
this.updatesDAO.values = new String[] {
|
||||||
|
null
|
||||||
|
};
|
||||||
|
this.system = new MockSystemStatus( );
|
||||||
|
this.registry = new MockRegistry( );
|
||||||
|
this.registry.put( new MaintenanceEnabler( this.system ) );
|
||||||
|
|
||||||
|
this.gup = new GameUpdateProcessorBean( );
|
||||||
|
this.gup.setLogger( this.logger );
|
||||||
|
this.gup.setUpdatesDAO( this.updatesDAO );
|
||||||
|
this.gup.setRegistry( this.registry );
|
||||||
|
this.gup.setTransactionManager( new MockTransactionManager( ) );
|
||||||
|
this.gup.setSystemStatus( this.system );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try locking the processor
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testLocking( )
|
||||||
|
{
|
||||||
|
assertTrue( this.gup.tryLock( ) );
|
||||||
|
assertFalse( this.gup.tryLock( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Try unlocking the processor */
|
||||||
|
@Test
|
||||||
|
public void testUnlocking( )
|
||||||
|
{
|
||||||
|
this.gup.tryLock( );
|
||||||
|
this.gup.unlock( );
|
||||||
|
assertTrue( this.gup.tryLock( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Try unlocking the processor when it's not locked */
|
||||||
|
@Test( expected = IllegalStateException.class )
|
||||||
|
public void testUnlockingWithNoLock( )
|
||||||
|
{
|
||||||
|
this.gup.unlock( );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GameUpdateProcessorBean#endPreviousCycle()} returns <code>false</code> when there is
|
||||||
|
* no "stuck" tick
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEndCycleNoStuckTick( )
|
||||||
|
{
|
||||||
|
assertFalse( this.gup.endPreviousCycle( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GameUpdateProcessorBean#endPreviousCycle()} returns <code>true</code> when maintenance
|
||||||
|
* mode is enabled, but the update is not processed
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEndCycleMaintenance( )
|
||||||
|
{
|
||||||
|
this.system.setCSTBehaviour( -2 );
|
||||||
|
assertTrue( this.gup.endPreviousCycle( ) );
|
||||||
|
assertEquals( 0 , this.updatesDAO.index );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GameUpdateProcessorBean#endPreviousCycle()} returns <code>true</code> when there is a
|
||||||
|
* stuck tick, and the update is processed
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEndCycle( )
|
||||||
|
{
|
||||||
|
this.system.setCSTBehaviour( 0 );
|
||||||
|
assertTrue( this.gup.endPreviousCycle( ) );
|
||||||
|
assertEquals( 1 , this.updatesDAO.index );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GameUpdateProcessorBean#executeUpdateCycle()} does not start a tick if there was a
|
||||||
|
* stuck tick, but the previous tick is processed.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testProcessWithStuckTick( )
|
||||||
|
{
|
||||||
|
this.system.setCSTBehaviour( 0 );
|
||||||
|
this.gup.executeUpdateCycle( );
|
||||||
|
assertFalse( this.system.wasStartTickCalled( ) );
|
||||||
|
assertEquals( 1 , this.updatesDAO.index );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GameUpdateProcessorBean#executeUpdateCycle()} does not start a tick if maintenance
|
||||||
|
* mode was enabled from the start
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testProcessWithStuckTickAndMaintenance( )
|
||||||
|
{
|
||||||
|
this.system.setCSTBehaviour( -2 );
|
||||||
|
this.gup.executeUpdateCycle( );
|
||||||
|
assertFalse( this.system.wasStartTickCalled( ) );
|
||||||
|
assertEquals( 0 , this.updatesDAO.index );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GameUpdateProcessorBean#executeUpdateCycle()} does not start a tick if maintenance
|
||||||
|
* mode is enabled between the check for stuck ticks and the new tick's start.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testProcessWithMaintenance( )
|
||||||
|
{
|
||||||
|
this.system.setSTBehaviour( -2 );
|
||||||
|
this.gup.executeUpdateCycle( );
|
||||||
|
assertTrue( this.system.wasStartTickCalled( ) );
|
||||||
|
assertEquals( 0 , this.updatesDAO.index );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GameUpdateProcessorBean#executeUpdateCycle()} throws a runtime exception if there
|
||||||
|
* seems to be a tick running when the new tick is supposed to start.
|
||||||
|
*/
|
||||||
|
@Test( expected = RuntimeException.class )
|
||||||
|
public void testProcessWithBadState( )
|
||||||
|
{
|
||||||
|
this.system.setSTBehaviour( -1 );
|
||||||
|
this.gup.executeUpdateCycle( );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link GameUpdateProcessorBean#executeUpdateCycle()} stops processing mid-way if maintenance
|
||||||
|
* mode becomes enabled
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testProcessWithMaintenanceEnabledMidWay( )
|
||||||
|
{
|
||||||
|
this.updatesDAO.values = new String[] {
|
||||||
|
null , "maintenance" , null
|
||||||
|
};
|
||||||
|
this.gup.executeUpdateCycle( );
|
||||||
|
assertTrue( this.system.wasStartTickCalled( ) );
|
||||||
|
assertEquals( 2 , this.updatesDAO.index );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.sys.Ticker.Frequency;
|
||||||
|
import com.deepclone.lw.testing.MockTicker;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link GameUpdateTaskBean}
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
public class TestGameUpdateTaskBean
|
||||||
|
{
|
||||||
|
/** The mock ticker */
|
||||||
|
private MockTicker ticker;
|
||||||
|
|
||||||
|
/** The mock game update processor */
|
||||||
|
private MockGameUpdateProcessor processor;
|
||||||
|
|
||||||
|
/** The game update task */
|
||||||
|
private GameUpdateTaskBean gut;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the mock components and the game update task instance
|
||||||
|
*/
|
||||||
|
@Before
|
||||||
|
public void setUp( )
|
||||||
|
{
|
||||||
|
this.ticker = new MockTicker( Frequency.MINUTE , "Game update" , GameUpdateTaskBean.class );
|
||||||
|
this.processor = new MockGameUpdateProcessor( );
|
||||||
|
|
||||||
|
this.gut = new GameUpdateTaskBean( );
|
||||||
|
this.gut.setTicker( this.ticker );
|
||||||
|
this.gut.setGameUpdateProcessor( this.processor );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the component's initialisation
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testInitialisation( )
|
||||||
|
{
|
||||||
|
this.gut.afterPropertiesSet( );
|
||||||
|
assertTrue( this.processor.wasTryLockCalled( ) );
|
||||||
|
assertTrue( this.processor.wasUnlockCalled( ) );
|
||||||
|
assertTrue( this.processor.wasEndCycleCalled( ) );
|
||||||
|
assertFalse( this.processor.wasExecuteCalled( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the component's run() method
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testNormalRun( )
|
||||||
|
{
|
||||||
|
this.gut.run( );
|
||||||
|
assertTrue( this.processor.wasTryLockCalled( ) );
|
||||||
|
assertTrue( this.processor.wasUnlockCalled( ) );
|
||||||
|
assertFalse( this.processor.wasEndCycleCalled( ) );
|
||||||
|
assertTrue( this.processor.wasExecuteCalled( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure the component is unlocked even if the execution fails
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testFailedRun( )
|
||||||
|
{
|
||||||
|
this.processor.setFailExecute( true );
|
||||||
|
try {
|
||||||
|
this.gut.run( );
|
||||||
|
fail( "Mock processor failed to fail" );
|
||||||
|
} catch ( RuntimeException e ) {
|
||||||
|
// EMPTY
|
||||||
|
}
|
||||||
|
assertTrue( this.processor.wasTryLockCalled( ) );
|
||||||
|
assertTrue( this.processor.wasUnlockCalled( ) );
|
||||||
|
assertFalse( this.processor.wasEndCycleCalled( ) );
|
||||||
|
assertTrue( this.processor.wasExecuteCalled( ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link GameUpdateTransaction}
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
public class TestGameUpdateTransaction
|
||||||
|
{
|
||||||
|
|
||||||
|
/** The mock updates DAO */
|
||||||
|
private MockUpdatesDAO updatesDAO;
|
||||||
|
|
||||||
|
/** The dummy batch processor used in the tests */
|
||||||
|
private DummyBatchProcessor processor;
|
||||||
|
|
||||||
|
/** The mock processor registry */
|
||||||
|
private MockRegistry registry;
|
||||||
|
|
||||||
|
/** The game update transaction used as a "guinea pig" */
|
||||||
|
private GameUpdateTransaction transaction;
|
||||||
|
|
||||||
|
|
||||||
|
/** Set up the mock objects and the guinea pig transaction instance */
|
||||||
|
@Before
|
||||||
|
public void setUp( )
|
||||||
|
{
|
||||||
|
this.updatesDAO = new MockUpdatesDAO( );
|
||||||
|
this.registry = new MockRegistry( );
|
||||||
|
this.registry.put( this.processor = new DummyBatchProcessor( "test" ) );
|
||||||
|
this.transaction = new GameUpdateTransaction( this.updatesDAO , this.registry , 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Test return value when the stored procedure says that no more updates are to be processed */
|
||||||
|
@Test
|
||||||
|
public void testWhenFinished( )
|
||||||
|
{
|
||||||
|
assertTrue( this.transaction.doInTransaction( null ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Test return value when the stored procedure says that more updates are to be processed */
|
||||||
|
@Test
|
||||||
|
public void testWhenHasMore( )
|
||||||
|
{
|
||||||
|
this.updatesDAO.values = new String[] {
|
||||||
|
null
|
||||||
|
};
|
||||||
|
assertFalse( this.transaction.doInTransaction( null ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Test what happens when the stored procedure indicates the need for local processing */
|
||||||
|
@Test
|
||||||
|
public void testLocalProcessing( )
|
||||||
|
{
|
||||||
|
this.updatesDAO.values = new String[] {
|
||||||
|
"test"
|
||||||
|
};
|
||||||
|
assertFalse( this.transaction.doInTransaction( null ) );
|
||||||
|
assertEquals( 1 , this.processor.getTicks( ).size( ) );
|
||||||
|
assertTrue( this.processor.getTicks( ).contains( 1L ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test what happens when the stored procedure indicates the need for local processing but the
|
||||||
|
* specified processor does not exist
|
||||||
|
*/
|
||||||
|
@Test( expected = UnsupportedUpdateException.class )
|
||||||
|
public void testMissingProcessor( )
|
||||||
|
{
|
||||||
|
this.updatesDAO.values = new String[] {
|
||||||
|
"missing processor"
|
||||||
|
};
|
||||||
|
this.transaction.doInTransaction( null );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package com.deepclone.lw.beans.updates;
|
||||||
|
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.BeanInitializationException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ServerProcessorRegistryBean}
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
public class TestServerProcessorRegistryBean
|
||||||
|
{
|
||||||
|
/** The "guinea pig" instance */
|
||||||
|
private ServerProcessorRegistryBean registry;
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp( )
|
||||||
|
{
|
||||||
|
this.registry = new ServerProcessorRegistryBean( );
|
||||||
|
this.registry.postProcessAfterInitialization( new DummyBatchProcessor( "test" ) , "test" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** The registry returns <code>null</code> when the type has no processor */
|
||||||
|
@Test
|
||||||
|
public void testMissingReturnsNull( )
|
||||||
|
{
|
||||||
|
assertNull( this.registry.getProcessorFor( "does not exist" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** The registry returns the processor when there is one */
|
||||||
|
@Test
|
||||||
|
public void testExistingReturnsProcessor( )
|
||||||
|
{
|
||||||
|
assertNotNull( this.registry.getProcessorFor( "test" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** The registry ignores objects which are not batch processors */
|
||||||
|
@Test
|
||||||
|
public void testIgnoresOtherObjects( )
|
||||||
|
{
|
||||||
|
this.registry.postProcessAfterInitialization( "test" , "test" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** The registry crashes when two batch processors are defined for the same type */
|
||||||
|
@Test( expected = BeanInitializationException.class )
|
||||||
|
public void testFailsIfDuplicate( )
|
||||||
|
{
|
||||||
|
this.registry.postProcessAfterInitialization( new DummyBatchProcessor( "test" ) , "test" );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.deepclone.lw.testing;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.eventlog.Logger;
|
||||||
|
import com.deepclone.lw.interfaces.eventlog.SystemLogger;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock Logger component
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MockLogger
|
||||||
|
implements Logger
|
||||||
|
{
|
||||||
|
/** Map of existing system loggers */
|
||||||
|
private final HashMap< String , MockSystemLogger > loggers = new HashMap< String , MockSystemLogger >( );
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SystemLogger getSystemLogger( String component )
|
||||||
|
{
|
||||||
|
MockSystemLogger logger = this.loggers.get( component );
|
||||||
|
if ( logger == null ) {
|
||||||
|
this.loggers.put( component , logger = new MockSystemLogger( ) );
|
||||||
|
}
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package com.deepclone.lw.testing;
|
||||||
|
|
||||||
|
|
||||||
|
import com.deepclone.lw.cmd.admin.logs.LogLevel;
|
||||||
|
import com.deepclone.lw.interfaces.eventlog.SystemLogger;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock logger for components
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
public class MockSystemLogger
|
||||||
|
implements SystemLogger
|
||||||
|
{
|
||||||
|
/** How many messages were logged */
|
||||||
|
private int counter = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/** @return the amount of messages that were logged */
|
||||||
|
public int getCounter( )
|
||||||
|
{
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the message counter
|
||||||
|
*
|
||||||
|
* @param counter
|
||||||
|
* the counter's new value
|
||||||
|
*/
|
||||||
|
public void setCounter( int counter )
|
||||||
|
{
|
||||||
|
this.counter = counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SystemLogger log( LogLevel level , String message )
|
||||||
|
{
|
||||||
|
this.counter++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SystemLogger log( LogLevel level , String message , Throwable exception )
|
||||||
|
{
|
||||||
|
this.counter++;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SystemLogger flush( )
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
package com.deepclone.lw.testing;
|
||||||
|
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.sys.MaintenanceData;
|
||||||
|
import com.deepclone.lw.interfaces.sys.MaintenanceStatusException;
|
||||||
|
import com.deepclone.lw.interfaces.sys.SystemStatus;
|
||||||
|
import com.deepclone.lw.interfaces.sys.TickStatusException;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock system status component
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
public class MockSystemStatus
|
||||||
|
implements SystemStatus
|
||||||
|
{
|
||||||
|
/** Maintenance data to return when {@link #checkMaintenance()} is called */
|
||||||
|
private MaintenanceData maintenance = null;
|
||||||
|
|
||||||
|
/** Value that determines the behaviour of {@link #startTick()} */
|
||||||
|
private long stValue = 0;
|
||||||
|
|
||||||
|
/** Value that determines the behaviour of {@link #checkStuckTick()} */
|
||||||
|
private long cstValue = -1;
|
||||||
|
|
||||||
|
/** Was {@link #startTick()} called? */
|
||||||
|
private boolean startTickCalled = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maintenance data to return when {@link #checkMaintenance()} is called
|
||||||
|
*
|
||||||
|
* @param maintenance
|
||||||
|
* the data to return from {@link #checkMaintenance()}
|
||||||
|
*/
|
||||||
|
public void setMaintenance( MaintenanceData maintenance )
|
||||||
|
{
|
||||||
|
this.maintenance = maintenance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value that determines the behaviour of {@link #checkStuckTick()}
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>-2 will cause a {@link MaintenanceStatusException},
|
||||||
|
* <li>-1 will cause it to return <code>null</code>,
|
||||||
|
* <li>any other value will be returned.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param cstValue
|
||||||
|
* the value
|
||||||
|
*/
|
||||||
|
public void setCSTBehaviour( long cstValue )
|
||||||
|
{
|
||||||
|
this.cstValue = cstValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the value that determines the behaviour of {@link #startTick()}
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>-2 will cause a {@link MaintenanceStatusException},
|
||||||
|
* <li>-1 will cause a {@link TickStatusException},
|
||||||
|
* <li>any other value will be returned.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param cstValue
|
||||||
|
* the value
|
||||||
|
*/
|
||||||
|
public void setSTBehaviour( long stValue )
|
||||||
|
{
|
||||||
|
this.stValue = stValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return <code>true</code> if {@link #startTick()} was called.
|
||||||
|
*/
|
||||||
|
public boolean wasStartTickCalled( )
|
||||||
|
{
|
||||||
|
return startTickCalled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MaintenanceData checkMaintenance( )
|
||||||
|
{
|
||||||
|
return this.maintenance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startMaintenance( int adminId , String reason , int duration )
|
||||||
|
throws MaintenanceStatusException
|
||||||
|
{
|
||||||
|
// EMPTY - ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateMaintenance( int adminId , int durationFromNow )
|
||||||
|
throws MaintenanceStatusException
|
||||||
|
{
|
||||||
|
// EMPTY - ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endMaintenance( int adminId )
|
||||||
|
throws MaintenanceStatusException
|
||||||
|
{
|
||||||
|
// EMPTY - ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long startTick( )
|
||||||
|
throws TickStatusException , MaintenanceStatusException
|
||||||
|
{
|
||||||
|
this.startTickCalled = true;
|
||||||
|
if ( this.stValue == -1 ) {
|
||||||
|
throw new TickStatusException( );
|
||||||
|
}
|
||||||
|
if ( this.stValue == -2 ) {
|
||||||
|
throw new MaintenanceStatusException( );
|
||||||
|
}
|
||||||
|
return this.stValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long checkStuckTick( )
|
||||||
|
throws MaintenanceStatusException
|
||||||
|
{
|
||||||
|
if ( this.cstValue == -1 ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ( this.cstValue == -2 ) {
|
||||||
|
throw new MaintenanceStatusException( );
|
||||||
|
}
|
||||||
|
return this.cstValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package com.deepclone.lw.testing;
|
||||||
|
|
||||||
|
|
||||||
|
import com.deepclone.lw.interfaces.sys.Ticker;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock ticker component
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* This mock component can be used to make sure that another component registers some task as
|
||||||
|
* expected.
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MockTicker
|
||||||
|
implements Ticker
|
||||||
|
{
|
||||||
|
/** The task's expected frequency */
|
||||||
|
private final Frequency expectedFrequency;
|
||||||
|
|
||||||
|
/** The task's expected name */
|
||||||
|
private final String expectedName;
|
||||||
|
|
||||||
|
/** The task's expected type */
|
||||||
|
private final Class< ? > expectedType;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the mock ticker's expectations
|
||||||
|
*
|
||||||
|
* @param expectedFrequency
|
||||||
|
* the task's expected frequency
|
||||||
|
* @param expectedName
|
||||||
|
* the task's expected name
|
||||||
|
* @param expectedType
|
||||||
|
* the task's expected type
|
||||||
|
*/
|
||||||
|
public MockTicker( Frequency expectedFrequency , String expectedName , Class< ? > expectedType )
|
||||||
|
{
|
||||||
|
this.expectedFrequency = expectedFrequency;
|
||||||
|
this.expectedName = expectedName;
|
||||||
|
this.expectedType = expectedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerTask( Frequency frequency , String name , Runnable task )
|
||||||
|
{
|
||||||
|
assertEquals( this.expectedFrequency , frequency );
|
||||||
|
assertEquals( this.expectedName , name );
|
||||||
|
assertTrue( this.expectedType.isInstance( task ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pause( )
|
||||||
|
throws IllegalStateException
|
||||||
|
{
|
||||||
|
// EMPTY - ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unpause( )
|
||||||
|
throws IllegalStateException
|
||||||
|
{
|
||||||
|
// EMPTY - ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive( )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.deepclone.lw.testing;
|
||||||
|
|
||||||
|
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
import org.springframework.transaction.TransactionDefinition;
|
||||||
|
import org.springframework.transaction.TransactionException;
|
||||||
|
import org.springframework.transaction.TransactionStatus;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mock transaction manager
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
|
||||||
|
*/
|
||||||
|
public class MockTransactionManager
|
||||||
|
implements PlatformTransactionManager
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransactionStatus getTransaction( TransactionDefinition definition )
|
||||||
|
throws TransactionException
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commit( TransactionStatus status )
|
||||||
|
throws TransactionException
|
||||||
|
{
|
||||||
|
// EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void rollback( TransactionStatus status )
|
||||||
|
throws TransactionException
|
||||||
|
{
|
||||||
|
// EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -168,6 +168,11 @@
|
||||||
<groupId>com.deepclone.lw</groupId>
|
<groupId>com.deepclone.lw</groupId>
|
||||||
<version>${legacyworlds.version.main}.${legacyworlds.version.release}-${legacyworlds.version.build}</version>
|
<version>${legacyworlds.version.main}.${legacyworlds.version.release}-${legacyworlds.version.build}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<artifactId>legacyworlds-server-beans-updates</artifactId>
|
||||||
|
<groupId>com.deepclone.lw</groupId>
|
||||||
|
<version>${legacyworlds.version.main}.${legacyworlds.version.release}-${legacyworlds.version.build}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<artifactId>legacyworlds-server-beans-user</artifactId>
|
<artifactId>legacyworlds-server-beans-user</artifactId>
|
||||||
<groupId>com.deepclone.lw</groupId>
|
<groupId>com.deepclone.lw</groupId>
|
||||||
|
|
Reference in a new issue