Importing SVN archives - Trunk

This commit is contained in:
Emmanuel BENOîT 2018-10-23 09:43:42 +02:00
parent fc4c6bd340
commit ff53af6668
507 changed files with 8866 additions and 2450 deletions

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View file

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>legacyworlds-server-beans-techs</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.maven.ide.eclipse.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.maven.ide.eclipse.maven2Nature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,6 @@
#Mon Mar 28 09:14:01 CEST 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.6

View file

@ -0,0 +1,9 @@
#Mon Mar 28 09:13:56 CEST 2011
activeProfiles=
eclipse.preferences.version=1
fullBuildGoals=process-test-resources
includeModules=false
resolveWorkspaceProjects=true
resourceFilterGoals=process-resources resources\:testResources
skipCompilerPlugin=true
version=1

View file

@ -0,0 +1,15 @@
<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>5.99.2</version>
</parent>
<groupId>com.deepclone.lw</groupId>
<artifactId>legacyworlds-server-beans-techs</artifactId>
<version>5.99.2</version>
<name>Legacy Worlds technology management</name>
<description>This package contains components which are used to manage technologies in LW games.</description>
</project>

View file

@ -0,0 +1,160 @@
package com.deepclone.lw.beans.techs;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import com.deepclone.lw.interfaces.game.techs.EmpireTechnologyDAO;
import com.deepclone.lw.sqld.game.techs.EmpireTechnology;
import com.deepclone.lw.utils.StoredProc;
/**
* Implementation the empire technology and research data access component.
*
* @author tseeker
*/
public class EmpireTechnologyDAOBean
implements EmpireTechnologyDAO
{
/** SQL query that reads the list of an empire's technologies */
private static final String qGetTechnologies = "SELECT * FROM emp.technologies_view WHERE empire = ?";
/** SQL query that executes the priorities update preparation function */
private static final String qStartPrioritiesUpdate = "SELECT emp.prepare_research_priorities_update( )";
/** SQL query that inserts research priority changes into the temporary table */
private static final String qUploadPriorities = "INSERT INTO research_priorities_updates ( technology , priority ) "
+ "VALUES ( ? , ? )";
/** Data source access component */
private SimpleJdbcTemplate dTemplate;
/** Row mapper for empire technologies */
private RowMapper< EmpireTechnology > mEmpireTechnology;
/** Wrapper for the stored procedure that implements technologies */
private StoredProc fImplementTech;
/** Wrapper for the stored procedure to apply research priority updates */
private StoredProc fApplyResearchPriorities;
/** Initialise the row mapper */
public EmpireTechnologyDAOBean( )
{
this.mEmpireTechnology = new RowMapper< EmpireTechnology >( ) {
@Override
public EmpireTechnology mapRow( ResultSet rs , int rowNum )
throws SQLException
{
int techId = rs.getInt( "technology_id" );
String techName = rs.getString( "technology" );
boolean detailed = rs.getBoolean( "detailed" );
Integer completion = (Integer) rs.getObject( "completion" );
Integer cost = (Integer) rs.getObject( "cost" );
Integer priority = (Integer) rs.getObject( "priority" );
EmpireTechnology result;
if ( !detailed ) {
result = new EmpireTechnology( techId * rs.getInt( "empire" ) , techName , completion , priority );
} else if ( completion != null ) {
result = new EmpireTechnology( techName , completion , priority );
} else if ( cost != null ) {
result = new EmpireTechnology( techName , cost );
} else {
result = new EmpireTechnology( techName );
}
return result;
}
};
}
/**
* Dependency injector that initialises the database access component and stored procedure
* wrappers.
*
* @param dataSource
* the data source
*/
@Autowired( required = true )
public void setDataSource( DataSource dataSource )
{
this.dTemplate = new SimpleJdbcTemplate( dataSource );
// Stored procedure for technology implementation
this.fImplementTech = new StoredProc( dataSource , "emp" , "implement_tech" );
this.fImplementTech.addParameter( "empire_id" , Types.INTEGER );
this.fImplementTech.addParameter( "technology" , Types.VARCHAR );
this.fImplementTech.addOutput( "error_code" , Types.INTEGER );
// Stored procedure to apply research priority updates
this.fApplyResearchPriorities = new StoredProc( dataSource , "emp" , "apply_research_priorities" );
this.fApplyResearchPriorities.addParameter( "empire_id" , Types.INTEGER );
this.fApplyResearchPriorities.addOutput( "error_code" , Types.INTEGER );
}
/* Documentation in EmpireTechnologyDAO interface */
@Override
public List< EmpireTechnology > getTechnologies( int empireId )
{
return this.dTemplate.query( qGetTechnologies , this.mEmpireTechnology , empireId );
}
/* Documentation in EmpireTechnologyDAO interface */
@Override
public int implementTechnology( int empireId , String technology )
{
return (Integer) this.fImplementTech.execute( empireId , technology ).get( "error_code" );
}
/* Documentation in EmpireTechnologyDAO interface */
@Override
public void startPrioritiesUpdate( )
{
this.dTemplate.queryForList( qStartPrioritiesUpdate );
}
/* Documentation in EmpireTechnologyDAO interface */
@Override
public void uploadPriorities( Map< String , Integer > priorities )
{
List< Object[] > batch = new ArrayList< Object[] >( priorities.size( ) );
int counter = 0;
for ( Entry< String , Integer > entry : priorities.entrySet( ) ) {
Object[] values = new Object[] {
entry.getKey( ) , entry.getValue( )
};
batch.add( counter++ , values );
}
this.dTemplate.batchUpdate( qUploadPriorities , batch );
}
/* Documentation in EmpireTechnologyDAO interface */
@Override
public int finishPrioritiesUpdate( int empireId )
{
return (Integer) this.fApplyResearchPriorities.execute( empireId ).get( "error_code" );
}
}

View file

@ -0,0 +1,181 @@
package com.deepclone.lw.beans.techs;
import java.util.Arrays;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.deepclone.lw.cmd.player.research.ResearchOperationResponse;
import com.deepclone.lw.cmd.player.research.ViewResearchResponse;
import com.deepclone.lw.interfaces.game.EmpireManagement;
import com.deepclone.lw.interfaces.game.techs.EmpireTechnologyDAO;
import com.deepclone.lw.interfaces.game.techs.EmpireTechnologyManager;
import com.deepclone.lw.interfaces.game.techs.TechnologyGraphManager;
import com.deepclone.lw.interfaces.sys.ConstantDefinition;
import com.deepclone.lw.interfaces.sys.ConstantsManager;
/**
* Implementation of the empire technology and research management component.
*
* @author tseeker
*/
@Transactional
public class EmpireTechnologyManagerBean
implements EmpireTechnologyManager
{
/** Empire management component */
private EmpireManagement empireManager;
/** Data access component for empire technologies and research */
private EmpireTechnologyDAO empireTechnologyDAO;
/** Technology graph management component */
private TechnologyGraphManager techGraphManager;
/**
* Dependency injector for the empire management component.
*
* @param empireManager
* the empire management component
*/
@Autowired( required = true )
public void setEmpireManager( EmpireManagement empireManager )
{
this.empireManager = empireManager;
}
/**
* Dependency injector for the data access component for empire technologies and research
*
* @param empireTechnologyDAO
* data access component for empire technologies and research
*/
@Autowired( required = true )
public void setEmpireTechnologyDAO( EmpireTechnologyDAO empireTechnologyDAO )
{
this.empireTechnologyDAO = empireTechnologyDAO;
}
/**
* Dependency injector for the technology graph management component.
*
* @param techGraphManager
* the technology graph management component
*/
@Autowired( required = true )
public void setTechnologyGraphManager( TechnologyGraphManager techGraphManager )
{
this.techGraphManager = techGraphManager;
}
/**
* Dependency injector which registers research-related constants into the constants manager.
*
* @param constantsManager
* the constants manager component
*/
@Autowired( required = true )
public void setConstantsManager( ConstantsManager constantsManager )
{
ConstantDefinition[] definitions = new ConstantDefinition[] {
new ConstantDefinition( "game.research.minPoints" , "Research" ,
"Minimal research points before a technology being researched is identified" , 50000.0 , 1.0 ,
true ) ,
new ConstantDefinition( "game.research.minRatio" , "Research" ,
"Minimal ratio before a technology being researched is identified" , 0.75 , 0.01 , 0.99 ) ,
new ConstantDefinition( "game.research.perPopUnit" , "Research" ,
"Research points per population unit." , 0.50 , 0.01 , true )
};
constantsManager.registerConstants( Arrays.asList( definitions ) );
}
/* Documentation in EmpireTechnologyManager interface */
@Override
public ViewResearchResponse getResearchData( int empireId )
{
return new ResearchResponseBuilder( empireId , this.empireManager , this.empireTechnologyDAO ,
this.techGraphManager ).getResponse( );
}
/* Documentation in EmpireTechnologyManager interface */
@Override
public ResearchOperationResponse implementTechnology( int empireId , String technology )
{
int result = this.empireTechnologyDAO.implementTechnology( empireId , technology );
ResearchOperationResponse response;
if ( result == 0 ) {
response = new ResearchOperationResponse( );
} else {
ResearchResponseBuilder builder = new ResearchResponseBuilder( empireId , this.empireManager ,
this.empireTechnologyDAO , this.techGraphManager );
switch ( result ) {
case 1:
response = builder.getResponse( ResearchOperationResponse.Result.ERR_RESOURCES );
break;
case 2:
response = builder.getResponse( ResearchOperationResponse.Result.ERR_STATE_CHANGED );
break;
default:
throw new RuntimeException( "unsupported return value " + result );
}
}
return response;
}
/* Documentation in EmpireTechnologyManager interface */
@Override
public ResearchOperationResponse setResearchPriorities( int empireId , Map< String , Integer > priorities )
{
int result;
if ( priorities.size( ) < 2 ) {
result = 2;
} else {
this.empireTechnologyDAO.startPrioritiesUpdate( );
this.empireTechnologyDAO.uploadPriorities( priorities );
result = this.empireTechnologyDAO.finishPrioritiesUpdate( empireId );
}
ResearchOperationResponse response;
if ( result == 0 ) {
response = new ResearchOperationResponse( );
} else {
ResearchResponseBuilder builder = new ResearchResponseBuilder( empireId , this.empireManager ,
this.empireTechnologyDAO , this.techGraphManager );
switch ( result ) {
case 1:
response = builder.getResponse( ResearchOperationResponse.Result.ERR_STATE_CHANGED );
break;
case 2:
response = builder.getResponse( ResearchOperationResponse.Result.ERR_INVALID );
break;
default:
throw new RuntimeException( "unsupported return value " + result );
}
}
return response;
}
}

View file

@ -0,0 +1,293 @@
package com.deepclone.lw.beans.techs;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.deepclone.lw.cmd.admin.logs.LogLevel;
import com.deepclone.lw.interfaces.eventlog.SystemLogger;
import com.deepclone.lw.sqld.game.techs.ResearchStatus;
import com.deepclone.lw.sqld.game.techs.ResearchUpdateInput;
import com.deepclone.lw.sqld.game.techs.ResearchUpdateOutput;
import com.deepclone.lw.sqld.game.techs.TechGraph;
import com.deepclone.lw.sqld.game.techs.TechGraphException;
import com.deepclone.lw.sqld.game.techs.Technology;
/**
* Research update implementation for a single empire.
*
* @author tseeker
*/
class EmpireUpdate
{
/** Empire identifier */
private final int id;
/** Research points produced */
private double production;
/** All technologies (known or being researched) */
private final Map< String , ResearchUpdateInput > byName;
/** Technology names by status */
private final Map< ResearchStatus , Set< String >> byStatus;
/** Output records by name */
private final Map< String , ResearchUpdateOutput > output;
/** Sum of the current research priorities */
private double totalPriority;
/** Amount of active research topics */
private int activeTopics;
/** Parent component's logging interface */
private SystemLogger logger;
/**
* Initialise the empire updater using the global lists of production points and research
* statuses.
*
* @param logger
* the component's logging system
* @param empireId
* the empire for whom the research update is to be computed
* @param production
* the map of production points for all empires in the batch
* @param status
* the list of statuses for all empires in the batch
*/
EmpireUpdate( SystemLogger logger , int empireId , Map< Integer , Double > production ,
HashMap< Integer , List< ResearchUpdateInput >> status )
{
this.id = empireId;
this.production = production.get( this.id );
this.logger = logger;
this.byName = new HashMap< String , ResearchUpdateInput >( );
this.output = new HashMap< String , ResearchUpdateOutput >( );
this.byStatus = new EnumMap< ResearchStatus , Set< String > >( ResearchStatus.class );
this.byStatus.put( ResearchStatus.IN_PROGRESS , new HashSet< String >( ) );
this.byStatus.put( ResearchStatus.RESEARCHED , new HashSet< String >( ) );
this.byStatus.put( ResearchStatus.IMPLEMENTED , new HashSet< String >( ) );
// Initialise sets and maps
List< ResearchUpdateInput > empStatus = status.get( this.id );
if ( empStatus != null ) {
for ( ResearchUpdateInput entry : status.get( this.id ) ) {
this.byName.put( entry.getTechnology( ) , entry );
this.byStatus.get( entry.getStatus( ) ).add( entry.getTechnology( ) );
}
this.logger.flush( );
}
}
/**
* Perform the research update, filling the {@link #output} map.
*
* @param techGraph
* the technology graph
* @throws TechGraphException
* indicates an internal error
*/
void compute( TechGraph techGraph )
throws TechGraphException
{
if ( this.production == 0 ) {
return;
}
this.prepareOutput( );
List< Technology > newResearch = this.findNewTechnologies( techGraph );
if ( !newResearch.isEmpty( ) ) {
this.addResearch( newResearch );
}
if ( this.output.isEmpty( ) ) {
return;
}
List< String > inProgress = new LinkedList< String >( this.output.keySet( ) );
for ( String resName : inProgress ) {
this.researchProgress( techGraph.getTechnology( resName ) , resName );
}
this.updatePriorities( );
}
/**
* Copy information from the list of research to the output map.
*/
private void prepareOutput( )
{
this.totalPriority = 0;
for ( ResearchUpdateInput entry : this.byName.values( ) ) {
if ( entry.getStatus( ) != ResearchStatus.IN_PROGRESS ) {
continue;
}
this.output.put( entry.getTechnology( ) , new ResearchUpdateOutput( this.id , entry.getTechnology( ) ,
false , entry.getPoints( ) , entry.getPriority( ) ) );
this.totalPriority += entry.getPriority( );
}
this.activeTopics = this.byStatus.get( ResearchStatus.IN_PROGRESS ).size( );
}
/**
* Find new technologies to research
*
* XXX: we shouldn't be doing that at every update; it's only required if the techs were added
* or deleted, if dependencies were changed or if the empire implemented a technology.
*
* @param techGraph
* the technology graph
* @return the list of technologies on which research can start
* @throws TechGraphException
* indicates an internal error
*/
private List< Technology > findNewTechnologies( TechGraph techGraph )
throws TechGraphException
{
LinkedList< Technology > newResearch = new LinkedList< Technology >( );
Set< String > implemented = this.byStatus.get( ResearchStatus.IMPLEMENTED );
if ( implemented == null ) {
throw new RuntimeException( "wtf?!" );
}
for ( String catName : techGraph.getCategories( ) ) {
for ( String techName : techGraph.getCategory( catName ).getTechnologies( ) ) {
// Check if the empire "knows" of the tech
if ( this.byName.containsKey( techName ) ) {
continue;
}
// Check dependencies
Technology tech = techGraph.getTechnology( techName );
boolean mayStart = true;
for ( String depName : tech.getDependencies( ) ) {
if ( !implemented.contains( depName ) ) {
mayStart = false;
break;
}
}
if ( mayStart ) {
newResearch.add( tech );
}
}
}
return newResearch;
}
/**
* Add new entries to the list of current research topics and updates the total priority
* accordingly.
*
* @param newResearch
* the list of research topics to add
*/
private void addResearch( List< Technology > newResearch )
{
int increment;
if ( this.totalPriority == 0 ) {
increment = 1;
} else {
increment = (int) Math.ceil( this.totalPriority / this.activeTopics );
}
for ( Technology tech : newResearch ) {
this.output.put( tech.getName( ) , new ResearchUpdateOutput( this.id , tech.getName( ) , true , 0 ,
increment ) );
this.totalPriority += increment;
}
this.activeTopics += newResearch.size( );
}
/**
* Update the progress on a research topic.
*
* @param technology
* the technology's definition
* @param name
* the name of the research topic
*/
private void researchProgress( Technology technology , String name )
{
ResearchUpdateOutput entry = this.output.get( name );
if ( entry.getPriority( ) == 0 ) {
return;
}
double points = this.production * entry.getPriority( ) / this.totalPriority;
double total = points + entry.getPoints( );
if ( total >= technology.getPoints( ) ) {
this.totalPriority -= entry.getPriority( );
this.production = this.production + total - entry.getPoints( );
this.output.put( name , new ResearchUpdateOutput( this.id , name ) );
this.activeTopics--;
} else {
entry.addPoints( points );
}
}
/**
* Update priorities of active research topics in the output map.
*/
private void updatePriorities( )
{
if ( this.activeTopics == 0 || this.totalPriority == 100 ) {
return;
}
List< ResearchUpdateOutput > progress = new LinkedList< ResearchUpdateOutput >( );
int newTotal = 0;
for ( ResearchUpdateOutput update : this.output.values( ) ) {
Integer prio = update.getPriority( );
if ( prio == null ) {
continue;
}
int newValue = (int) Math.floor( 100.0 * prio / this.totalPriority );
progress.add( update );
update.setPriority( newValue );
newTotal += newValue;
}
// Distribute the rest of the points
while ( newTotal < 100 ) {
for ( ResearchUpdateOutput update : progress ) {
update.setPriority( update.getPriority( ) + 1 );
newTotal++;
if ( newTotal == 100 ) {
break;
}
}
}
this.logger.flush( );
}
/** @return the update output records */
Collection< ResearchUpdateOutput > getOutput( )
{
if ( !this.output.isEmpty( ) ) {
this.logger.log( LogLevel.TRACE , "Empire " + this.id + " - " + this.output.size( ) + " update(s)" )
.flush( );
}
return this.output.values( );
}
}

View file

@ -0,0 +1,280 @@
package com.deepclone.lw.beans.techs;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.deepclone.lw.cmd.player.gdata.GamePageData;
import com.deepclone.lw.cmd.player.gdata.empire.ResearchData;
import com.deepclone.lw.cmd.player.gdata.empire.TechnologyCategoryData;
import com.deepclone.lw.cmd.player.gdata.empire.TechnologyData;
import com.deepclone.lw.cmd.player.research.ResearchOperationResponse;
import com.deepclone.lw.cmd.player.research.ViewResearchResponse;
import com.deepclone.lw.cmd.player.research.ResearchOperationResponse.Result;
import com.deepclone.lw.interfaces.game.EmpireManagement;
import com.deepclone.lw.interfaces.game.techs.EmpireTechnologyDAO;
import com.deepclone.lw.interfaces.game.techs.TechnologyGraphManager;
import com.deepclone.lw.interfaces.i18n.LanguageTranslator;
import com.deepclone.lw.interfaces.i18n.UnknownStringException;
import com.deepclone.lw.sqld.game.techs.Category;
import com.deepclone.lw.sqld.game.techs.EmpireTechnology;
import com.deepclone.lw.sqld.game.techs.ResearchStatus;
import com.deepclone.lw.sqld.game.techs.TechGraph;
import com.deepclone.lw.sqld.game.techs.TechGraphException;
import com.deepclone.lw.sqld.game.techs.Technology;
/**
* Class that builds {@link ViewResearchResponse} or {@link ResearchOperationResponse} instances.
*
* @author tseeker
*/
class ResearchResponseBuilder
{
/** Empire identifier */
private final int empireId;
/** Empire management component */
private final EmpireManagement empireManager;
/** Technology and research data access component */
private final EmpireTechnologyDAO empireTechnologyDAO;
/** Technology graph management component */
private final TechnologyGraphManager techGraphManager;
/** Game page data */
private GamePageData pageData;
/** List of current research topics */
private List< ResearchData > research;
/** Lists of implemented technologies by category */
private Map< String , List< TechnologyData > > implementedMap;
/** Final list of implemented technologies */
private List< TechnologyCategoryData > implementedLists;
/** All implemented technologies by identifier */
private Map< String , TechnologyData > implementedById;
/**
* Initialise the response builder by setting the empire identifier and the various components
* that may be required to generate the response.
*
* @param empireId
* the empire identifier
* @param empireManager
* the empire management component
* @param empireTechnologyDAO
* the technology and research data access component
* @param techGraphManager
* the technology graph management component
*/
ResearchResponseBuilder( int empireId , EmpireManagement empireManager , EmpireTechnologyDAO empireTechnologyDAO ,
TechnologyGraphManager techGraphManager )
{
this.empireId = empireId;
this.empireManager = empireManager;
this.empireTechnologyDAO = empireTechnologyDAO;
this.techGraphManager = techGraphManager;
}
/**
* Build a basic research view response.
*
* @return the research view response.
*/
ViewResearchResponse getResponse( )
{
try {
this.buildResponseContents( );
} catch ( Exception e ) {
throw new RuntimeException( "internal error while processing research data" , e );
}
return new ViewResearchResponse( this.pageData , this.research , this.implementedLists );
}
/**
* Build a research operation response.
*
* @param errorCode
* the operation's error code (should not be
* {@link ResearchOperationResponse.Result#OK}).
* @return the research operation response.
*/
ResearchOperationResponse getResponse( Result errorCode )
{
try {
this.buildResponseContents( );
} catch ( Exception e ) {
throw new RuntimeException( "internal error while processing research data" , e );
}
return new ResearchOperationResponse( this.pageData , this.research , this.implementedLists , errorCode );
}
/**
* Build the lists and records required by research view or operation responses. This includes
* the list of current research topics as well as the list of implemented technologies.
*
* @throws UnknownStringException
* if there is an incoherence between the tech graph and the translations database.
* @throws TechGraphException
* if there is an incoherence between the empire research records and the tech
* graph.
*/
private void buildResponseContents( )
throws UnknownStringException , TechGraphException
{
LanguageTranslator translator = this.empireManager.getTranslator( this.empireId );
TechGraph techGraph = this.techGraphManager.getGraph( );
this.research = new LinkedList< ResearchData >( );
this.implementedMap = new HashMap< String , List< TechnologyData > >( );
this.implementedById = new HashMap< String , TechnologyData >( );
for ( EmpireTechnology record : this.empireTechnologyDAO.getTechnologies( this.empireId ) ) {
if ( record.getStatus( ) == ResearchStatus.IMPLEMENTED ) {
this.addImplemented( record , translator , techGraph );
} else {
this.addResearchData( record , translator , techGraph );
}
}
this.pageData = this.empireManager.getGeneralInformation( this.empireId );
Collections.sort( this.research );
this.postProcessImplemented( translator , techGraph );
}
/**
* Add an implemented technology record.
*
* @param record
* the original record as returned by the {@link EmpireTechnologyDAO}.
* @param translator
* the translator for the empire's language
* @param techGraph
* the technology graph
*
* @throws TechGraphException
* if there is an incoherence between the empire research records and the tech
* graph.
* @throws UnknownStringException
* if there is an incoherence between the tech graph and the translations database.
*/
private void addImplemented( EmpireTechnology record , LanguageTranslator translator , TechGraph techGraph )
throws TechGraphException , UnknownStringException
{
String techId = record.getIdentifier( );
Technology tech = techGraph.getTechnology( techId );
Category category = tech.getCategory( );
List< TechnologyData > catTechs = this.implementedMap.get( category.getName( ) );
if ( catTechs == null ) {
catTechs = new LinkedList< TechnologyData >( );
this.implementedMap.put( category.getName( ) , catTechs );
}
TechnologyData tData;
String name = translator.translate( techId );
String description = translator.translate( tech.getDescription( ) );
tData = new TechnologyData( techId , name , description , tech.getDependencies( ) , new LinkedList< String >( ) );
catTechs.add( tData );
this.implementedById.put( techId , tData );
}
/**
* Add a research topic record.
*
* @param record
* the original record as returned by the {@link EmpireTechnologyDAO}.
* @param translator
* the translator for the empire's language
* @param techGraph
* the technology graph
*
* @throws TechGraphException
* if there is an incoherence between the empire research records and the tech
* graph.
* @throws UnknownStringException
* if there is an incoherence between the tech graph and the translations database.
*/
private void addResearchData( EmpireTechnology record , LanguageTranslator translator , TechGraph techGraph )
throws TechGraphException , UnknownStringException
{
String techId = record.getIdentifier( );
Technology tech = techGraph.getTechnology( techId );
String category = translator.translate( tech.getCategory( ).getName( ) );
ResearchData rData;
if ( !record.isDetailed( ) ) {
rData = new ResearchData( record.getNumericId( ) , category , record.getPercentage( ) , record
.getPriority( ) );
} else {
String name = translator.translate( techId );
String description = translator.translate( tech.getDescription( ) );
String[] dependencies = tech.getDependencies( ).toArray( new String[] { } );
if ( record.getStatus( ) == ResearchStatus.RESEARCHED ) {
rData = new ResearchData( techId , category , name , description , dependencies , (int) tech.getCost( ) );
} else {
rData = new ResearchData( techId , category , name , description , dependencies , record
.getPercentage( ) , record.getPriority( ) );
}
}
this.research.add( rData );
}
/**
* Post-process implemented technologies by adding reverse dependencies, sorting each list, and
* generating the sorted list of technology categories.
*
* @param translator
* the translator for the empire's language
* @param techGraph
* the technology graph
*
* @throws TechGraphException
* if there is an incoherence between the empire research records and the tech
* graph.
* @throws UnknownStringException
* if there is an incoherence between the tech graph and the translations database.
*/
private void postProcessImplemented( LanguageTranslator translator , TechGraph techGraph )
throws UnknownStringException , TechGraphException
{
this.implementedLists = new LinkedList< TechnologyCategoryData >( );
for ( String catId : this.implementedMap.keySet( ) ) {
List< TechnologyData > techs = this.implementedMap.get( catId );
for ( TechnologyData tData : techs ) {
for ( String dependency : tData.getDependsOn( ) ) {
TechnologyData depData = this.implementedById.get( dependency );
if ( depData != null ) {
depData.getDependencyOf( ).add( tData.getIdentifier( ) );
}
}
}
Collections.sort( techs );
String catName = translator.translate( catId );
String catDesc = translator.translate( techGraph.getCategory( catId ).getDescription( ) );
this.implementedLists.add( new TechnologyCategoryData( catName , catDesc , techs ) );
}
Collections.sort( this.implementedLists );
}
}

View file

@ -0,0 +1,180 @@
package com.deepclone.lw.beans.techs;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import com.deepclone.lw.interfaces.eventlog.Logger;
import com.deepclone.lw.interfaces.eventlog.SystemLogger;
import com.deepclone.lw.interfaces.game.techs.ResearchUpdateDAO;
import com.deepclone.lw.interfaces.game.techs.TechnologyGraphManager;
import com.deepclone.lw.interfaces.game.updates.GameUpdate;
import com.deepclone.lw.interfaces.game.updates.GameUpdatePhase;
import com.deepclone.lw.interfaces.game.updates.GameUpdatePhaseHandler;
import com.deepclone.lw.interfaces.game.updates.UpdatesDAO;
import com.deepclone.lw.sqld.game.techs.ResearchUpdateInput;
import com.deepclone.lw.sqld.game.techs.ResearchUpdateOutput;
import com.deepclone.lw.sqld.game.techs.TechGraph;
import com.deepclone.lw.sqld.game.techs.TechGraphException;
/**
* Component responsible for updating empires' research status.
*
* @author tseeker
*/
public class ResearchUpdateBean
implements GameUpdatePhaseHandler
{
/** The component's logging interface */
private SystemLogger logger;
/** The technology graph management component */
private TechnologyGraphManager techGraphManager;
/** The current technology graph */
private TechGraph techGraph;
/** The main game update data access component */
private UpdatesDAO updatesDAO;
/** The research updates data access component */
private ResearchUpdateDAO researchUpdateDAO;
/**
* Dependency injector that initialises the component's logging interface.
*
* @param logger
* the system logger
*/
@Autowired( required = true )
public void setLogger( Logger logger )
{
this.logger = logger.getSystemLogger( "ResearchUpdate" );
}
/**
* Dependency injector for the technology graph management component.
*
* @param techGraphManager
* the technology graph management component
*/
@Autowired( required = true )
public void setTechGraphManager( TechnologyGraphManager techGraphManager )
{
this.techGraphManager = techGraphManager;
}
/**
* Dependency injector that sets the main game update data access component.
*
* @param updatesDAO
* the main game update data access component
*/
@Autowired( required = true )
public void setUpdatesDAO( UpdatesDAO updatesDAO )
{
this.updatesDAO = updatesDAO;
}
/**
* Dependency injector that sets the research updates data access component
*
* @param researchUpdateDAO
* the research updates data access component
*/
@Autowired( required = true )
public void setResearchUpdateDAO( ResearchUpdateDAO researchUpdateDAO )
{
this.researchUpdateDAO = researchUpdateDAO;
}
/**
* Dependency injector that registers the handler with the main game update manager
*
* @param updateManager
* the game update manager
*/
@Autowired( required = true )
public void setUpdateManager( GameUpdate updateManager )
{
updateManager.registerHandler( this );
}
/* Documentation in GameUpdatePhaseHandler interface */
@Override
public GameUpdatePhase getPhase( )
{
return GameUpdatePhase.EMPIRE_RESEARCH;
}
/**
* Get a copy of the technology graph when the update phase starts.
*/
@Override
public void onPhaseStart( long updateId )
{
this.techGraph = this.techGraphManager.getGraph( );
}
/**
* If there are research updates left to process, lock the records, then obtain all relevant
* data, process each empire using {@link EmpireUpdate}, and send the updates to the database.
*/
@Override
public boolean updateGame( long updateId )
{
// Look for records to update
if ( !this.updatesDAO.prepareUpdates( updateId , this.getPhase( ) ) ) {
return false;
}
Map< Integer , Double > production;
HashMap< Integer , List< ResearchUpdateInput >> status;
status = new HashMap< Integer , List< ResearchUpdateInput > >( );
// Prepare update and load input
this.researchUpdateDAO.prepareUpdate( updateId );
production = this.researchUpdateDAO.getResearchPoints( updateId );
for ( ResearchUpdateInput input : this.researchUpdateDAO.getUpdateData( updateId ) ) {
List< ResearchUpdateInput > eStatus = status.get( input.getEmpireId( ) );
if ( eStatus == null ) {
eStatus = new LinkedList< ResearchUpdateInput >( );
status.put( input.getEmpireId( ) , eStatus );
}
eStatus.add( input );
}
// Handle the update for each empire
List< ResearchUpdateOutput > output = new LinkedList< ResearchUpdateOutput >( );
for ( int empireId : production.keySet( ) ) {
EmpireUpdate update = new EmpireUpdate( this.logger , empireId , production , status );
try {
update.compute( this.techGraph );
} catch ( TechGraphException e ) {
throw new RuntimeException( "incoherent technology graph" , e );
}
output.addAll( update.getOutput( ) );
}
// Send and validate updates
this.researchUpdateDAO.submitUpdateData( output );
this.updatesDAO.validateUpdatedRecords( updateId , GameUpdatePhase.EMPIRE_RESEARCH );
return true;
}
}

View file

@ -0,0 +1,138 @@
package com.deepclone.lw.beans.techs;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import com.deepclone.lw.interfaces.game.techs.ResearchUpdateDAO;
import com.deepclone.lw.sqld.game.techs.ResearchUpdateInput;
import com.deepclone.lw.sqld.game.techs.ResearchUpdateOutput;
import com.deepclone.lw.utils.StoredProc;
public class ResearchUpdateDAOBean
implements ResearchUpdateDAO
{
/** Query that reads research update input */
private static final String qGetInput = "SELECT * FROM emp.research_update_input_view WHERE update_id = ?";
/** Query that reads empire research points production */
private static final String qGetPoints = "SELECT * FROM emp.research_points_production WHERE update_id = ?";
/** Query that adds a record to the output */
private static final String qAddOutput = "INSERT INTO research_update_output "
+ "(empire_id , technology , creation , points , priority) "
+ "VALUES ( :empireId , :technology , :creation , :points , :priority )";
/** Query that submits the update */
private static final String qSubmitUpdate = "SELECT emp.submit_research_update( )";
/** Data source access */
private SimpleJdbcTemplate dTemplate;
/** Wrapper for the stored procedure that prepares the database for update */
private StoredProc fPrepareUpdate;
/** Row mapper for research update input */
RowMapper< ResearchUpdateInput > ruiMapper;
/**
* Initialise the component's row mapper.
*/
public ResearchUpdateDAOBean( )
{
this.ruiMapper = new RowMapper< ResearchUpdateInput >( ) {
@Override
public ResearchUpdateInput mapRow( ResultSet rs , int rowNum )
throws SQLException
{
int empireId = rs.getInt( "empire_id" );
String technology = rs.getString( "technology" );
Boolean implemented = (Boolean) rs.getObject( "implemented" );
if ( implemented == null ) {
double points = rs.getDouble( "points" );
int priority = rs.getInt( "priority" );
return new ResearchUpdateInput( empireId , technology , points , priority );
}
return new ResearchUpdateInput( empireId , technology , implemented );
}
};
}
/**
* Dependency injector that initialises the component's database access and stored procedure
* wrappers.
*
* @param dataSource
* the data source
*/
@Autowired( required = true )
public void setDataSource( DataSource dataSource )
{
this.dTemplate = new SimpleJdbcTemplate( dataSource );
// Stored procedure that prepares the database for update
this.fPrepareUpdate = new StoredProc( dataSource , "emp" , "prepare_research_update" );
this.fPrepareUpdate.addParameter( "update_id" , Types.BIGINT );
}
/* Documented in ResearchUpdateDAO interface */
@Override
public void prepareUpdate( long updateId )
{
this.fPrepareUpdate.execute( updateId );
}
/* Documented in ResearchUpdateDAO interface */
@Override
public List< ResearchUpdateInput > getUpdateData( long updateId )
{
return this.dTemplate.query( qGetInput , this.ruiMapper , updateId );
}
/* Documented in ResearchUpdateDAO interface */
@Override
public Map< Integer , Double > getResearchPoints( long updateId )
{
Map< Integer , Double > result = new HashMap< Integer , Double >( );
for ( Map< String , Object > row : this.dTemplate.queryForList( qGetPoints , updateId ) ) {
result.put( (Integer) row.get( "empire_id" ) , (Double) row.get( "points" ) );
}
return result;
}
/* Documented in ResearchUpdateDAO interface */
@Override
public void submitUpdateData( List< ResearchUpdateOutput > output )
{
if ( !output.isEmpty( ) ) {
SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch( output.toArray( ) );
this.dTemplate.batchUpdate( qAddOutput , batch );
}
this.dTemplate.queryForList( qSubmitUpdate );
}
}

View file

@ -0,0 +1,134 @@
package com.deepclone.lw.beans.techs;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import com.deepclone.lw.interfaces.game.techs.TechnologyGraphDAO;
import com.deepclone.lw.sqld.game.techs.TechGraph;
import com.deepclone.lw.sqld.game.techs.TechGraphException;
public class TechnologyGraphDAOBean
implements TechnologyGraphDAO
{
private static final String qGetCategories = "SELECT name , description FROM tech.categories_view";
private static final String qGetTechnologies = "SELECT category , name , description , points , cost FROM tech.technologies_view";
private static final String qGetDependencies = "SELECT technology , dependency FROM tech.dependencies_view";
private static abstract class TechGraphDBHandler
implements RowCallbackHandler
{
TechGraph graph;
TechGraphException error;
public TechGraphDBHandler( )
{
// EMPTY
}
public final void setGraph( TechGraph graph )
{
this.graph = graph;
}
@Override
public final void processRow( ResultSet rs )
throws SQLException
{
if ( this.error != null ) {
return;
}
try {
this.extractData( rs );
} catch ( TechGraphException e ) {
this.error = e;
}
}
protected abstract void extractData( ResultSet rs )
throws TechGraphException , SQLException;
}
private static class CategoryExtractor
extends TechGraphDBHandler
{
@Override
protected void extractData( ResultSet rs )
throws TechGraphException , SQLException
{
this.graph.addCategory( rs.getString( "name" ) , rs.getString( "description" ) );
}
}
private static class TechnologyExtractor
extends TechGraphDBHandler
{
@Override
protected void extractData( ResultSet rs )
throws TechGraphException , SQLException
{
this.graph.addTechnology( rs.getString( "category" ) , rs.getString( "name" ) , rs
.getString( "description" ) , rs.getInt( "points" ) , rs.getInt( "cost" ) );
}
}
private static class DependencyExtractor
extends TechGraphDBHandler
{
@Override
protected void extractData( ResultSet rs )
throws TechGraphException , SQLException
{
this.graph.addDependency( rs.getString( "technology" ) , rs.getString( "dependency" ) );
}
}
private JdbcTemplate dTemplate;
@Autowired( required = true )
public void setDataSource( DataSource dataSource )
{
this.dTemplate = new JdbcTemplate( dataSource );
}
@Override
public TechGraph loadGraph( )
throws TechGraphException
{
TechGraph result = new TechGraph( );
this.extractGraphData( result , new CategoryExtractor( ) , qGetCategories );
this.extractGraphData( result , new TechnologyExtractor( ) , qGetTechnologies );
this.extractGraphData( result , new DependencyExtractor( ) , qGetDependencies );
return result;
}
private void extractGraphData( TechGraph graph , TechGraphDBHandler extractor , String query )
throws TechGraphException
{
extractor.setGraph( graph );
this.dTemplate.query( query , extractor );
if ( extractor.error != null ) {
throw extractor.error;
}
}
}

View file

@ -0,0 +1,74 @@
package com.deepclone.lw.beans.techs;
import java.util.LinkedList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.deepclone.lw.cmd.admin.techs.TechCategory;
import com.deepclone.lw.interfaces.game.techs.TechnologyGraphDAO;
import com.deepclone.lw.interfaces.game.techs.TechnologyGraphManager;
import com.deepclone.lw.sqld.game.techs.Category;
import com.deepclone.lw.sqld.game.techs.TechGraph;
import com.deepclone.lw.sqld.game.techs.TechGraphException;
@Transactional
public class TechnologyGraphManagerBean
implements TechnologyGraphManager
{
private TechGraph graph;
private TechnologyGraphDAO technologyGraphDAO;
@Autowired( required = true )
public void setTechGraphDAO( TechnologyGraphDAO technologyGraphDAO )
{
this.technologyGraphDAO = technologyGraphDAO;
}
private void loadGraph( )
throws TechGraphException
{
if ( this.graph == null ) {
this.graph = this.technologyGraphDAO.loadGraph( );
}
}
/* Documented in TechnologyGraphManager interface */
@Override
public TechGraph getGraph( )
{
try {
this.loadGraph( );
} catch ( TechGraphException e ) {
throw new RuntimeException( e );
}
return new TechGraph( this.graph );
}
/* Documented in TechnologyGraphManager interface */
@Override
public List< TechCategory > listCategories( )
{
try {
this.loadGraph( );
List< TechCategory > result = new LinkedList< TechCategory >( );
for ( String catName : this.graph.getCategories( ) ) {
Category cat = this.graph.getCategory( catName );
result.add( new TechCategory( catName , cat.getDescription( ) , cat.getTechnologies( ) ) );
}
return result;
} catch ( TechGraphException e ) {
throw new RuntimeException( e );
}
}
}

View file

@ -0,0 +1,14 @@
<?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">
<import resource="techs/empire-technology-dao-bean.xml" />
<import resource="techs/empire-technology-manager-bean.xml" />
<import resource="techs/research-update-bean.xml" />
<import resource="techs/research-update-dao-bean.xml" />
<import resource="techs/technology-graph-dao-bean.xml" />
<import resource="techs/technology-graph-manager-bean.xml" />
</beans>

View file

@ -0,0 +1,8 @@
<?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="empireTechnologyDAO" class="com.deepclone.lw.beans.techs.EmpireTechnologyDAOBean" />
</beans>

View file

@ -0,0 +1,8 @@
<?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="empireTechnologyManager" class="com.deepclone.lw.beans.techs.EmpireTechnologyManagerBean" />
</beans>

View file

@ -0,0 +1,8 @@
<?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="researchUpdate" class="com.deepclone.lw.beans.techs.ResearchUpdateBean" />
</beans>

View file

@ -0,0 +1,8 @@
<?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="researchUpdateDAO" class="com.deepclone.lw.beans.techs.ResearchUpdateDAOBean" />
</beans>

View file

@ -0,0 +1,8 @@
<?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="technologyGraphDAO" class="com.deepclone.lw.beans.techs.TechnologyGraphDAOBean" />
</beans>

View file

@ -0,0 +1,8 @@
<?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="technologyGraphManager" class="com.deepclone.lw.beans.techs.TechnologyGraphManagerBean" />
</beans>