Research update

* Implemented new research update. Old research system no longer
updates.

* Fixed a major bug in the constants registrar

* Added game.research.vacation constant (determines the rate of
research when players are in vacation mode)
This commit is contained in:
Emmanuel BENOîT 2012-04-02 13:29:43 +02:00
parent a14601df37
commit c7949e41cc
11 changed files with 414 additions and 85 deletions
legacyworlds-server-data/db-structure/parts

View file

@ -21,7 +21,7 @@
* _empire The empire's identifier
* _technology The string identifier for the technology to implement
*/
DROP FUNCTION emp.technology_implement( INT , TEXT );
DROP FUNCTION IF EXISTS emp.technology_implement( INT , TEXT );
CREATE FUNCTION emp.technology_implement( _empire INT , _technology TEXT )
RETURNS BOOLEAN
LANGUAGE PLPGSQL
@ -291,6 +291,56 @@ GRANT EXECUTE
/*
* Compute an empire's total research points
* ------------------------------------------
*
* Obtain an empire's total research points by adding the happiness-adjusted
* value for each planet, then applying the global modifier for vacation mode
* if necessary.
*
* FIXME: time factor is hard-coded
*
* Parameters:
* _empire The empire's identifier
* _on_vacation TRUE if the player is on vacation, FALSE otherwise
*
* Returns:
* ? The amount of research points.
*/
DROP FUNCTION IF EXISTS emp.research_get_points( INT , BOOLEAN );
CREATE FUNCTION emp.research_get_points( _empire INT , _on_vacation BOOLEAN )
RETURNS DOUBLE PRECISION
LANGUAGE SQL
STRICT STABLE
SECURITY INVOKER
AS $research_get_points$
SELECT SUM( verse.adjust_production(
_planet.population * sys.get_constant( 'game.research.basePoints' ) ,
_happiness.current / _planet.population
) ) * ( CASE
WHEN $2 THEN
sys.get_constant( 'game.research.vacation' )
ELSE
1.0
END )::DOUBLE PRECISION
FROM emp.planets _emp_planet
INNER JOIN verse.planets _planet
ON _emp_planet.planet_id = _planet.name_id
INNER JOIN verse.planet_happiness _happiness
USING ( planet_id )
WHERE _emp_planet.empire_id = $1;
$research_get_points$;
REVOKE EXECUTE
ON FUNCTION emp.research_get_points( INT , BOOLEAN )
FROM PUBLIC;
/*
* Technology visibility view

View file

@ -3,90 +3,188 @@
--
-- Game updates - empire research
--
-- Copyright(C) 2004-2010, DeepClone Development
-- Copyright(C) 2004-2012, DeepClone Development
-- --------------------------------------------------------
CREATE OR REPLACE FUNCTION sys.process_empire_research_updates( c_tick BIGINT )
/*
* Empire update data
* -------------------
*
* This type can be used to return empires along with their "on vacation"
* status.
*
* FIXME: it should probably be somewhere else, but for now this is the only
* file that uses it.
*/
DROP TYPE IF EXISTS sys.empire_update_type CASCADE;
CREATE TYPE sys.empire_update_type AS (
/* The empire's identifier */
empire_id INT ,
/* TRUE if the player is on vacation, FALSE otherwise */
on_vacation BOOLEAN
);
/*
* Lock records and list empires which require a research update
* --------------------------------------------------------------
*
* This function will lock all records needed for a research update, and
* return the list of empires to be updated. These empires, in addition to
* being marked for update, must possess planets and have in-progress
* research.
*
* Parameters:
* _tick The identifier of the current update cycle
*
* Returns a set of:
* empire_id The empire's identifier
* on_vacation TRUE if the player is on vacation, FALSE otherwise
*/
DROP FUNCTION IF EXISTS sys.gu_research_get_empires( BIGINT ) CASCADE;
CREATE FUNCTION sys.gu_research_get_empires( _tick BIGINT )
RETURNS SETOF sys.empire_update_type
LANGUAGE SQL
STRICT VOLATILE
SECURITY INVOKER
AS $gu_research_get_empires$
SELECT DISTINCT * FROM (
SELECT _empire.name_id AS empire_id ,
( _vacation.status IS NOT NULL AND
_vacation.status = 'PROCESSED' ) AS on_vacation
FROM sys.updates _upd_sys
INNER JOIN emp.empires_updates _emp_update
USING ( updtgt_id , updtype_id , update_id )
INNER JOIN emp.empires _empire
USING ( name_id )
INNER JOIN emp.technologies_v2 _emp_tech
ON _emp_tech.empire_id = _empire.name_id
INNER JOIN defs.technologies _tech
USING ( technology_name_id )
INNER JOIN emp.planets _emp_planet
USING ( empire_id )
INNER JOIN verse.planets _planet
ON _emp_planet.planet_id = _planet.name_id
INNER JOIN verse.planet_happiness _happiness
USING ( planet_id )
INNER JOIN naming.empire_names _emp_name
ON _emp_name.id = _empire.name_id
INNER JOIN users.credentials _user
ON _user.address_id = _emp_name.owner_id
LEFT OUTER JOIN users.vacations _vacation
ON _vacation.account_id = _emp_name.owner_id
WHERE _upd_sys.update_last = $1
AND _upd_sys.update_state = 'PROCESSING'
AND _emp_tech.emptech_state = 'RESEARCH'
FOR UPDATE OF _upd_sys , _emp_update , _emp_tech
FOR SHARE OF _empire , _tech , _emp_planet , _planet ,
_happiness , _emp_name , _user
) _sub;
$gu_research_get_empires$;
REVOKE EXECUTE
ON FUNCTION sys.gu_research_get_empires( BIGINT )
FROM PUBLIC;
/*
* Update research for a single empire
* ------------------------------------
*
* This stored procedure updates research points for all in-progress research
* of a single empire.
*
* Parameters:
* _empire_id The empire's identifier
* _on_vacation TRUE if the player is on vacation, FALSE otherwise
*/
DROP FUNCTION IF EXISTS sys.gu_research_update_empire( INT , BOOLEAN ) CASCADE;
CREATE FUNCTION sys.gu_research_update_empire( _empire INT , _on_vacation BOOLEAN )
RETURNS VOID
LANGUAGE PLPGSQL
STRICT VOLATILE
SECURITY INVOKER
AS $$
AS $gu_research_update_empire$
DECLARE
rec RECORD;
r_points REAL;
tu_rec RECORD;
_points DOUBLE PRECISION;
_record RECORD;
BEGIN
-- Lock empires for update and planets for share
PERFORM e.name_id
FROM sys.updates _upd_sys
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 verse.planets p ON p.name_id = ep.planet_id
WHERE _upd_sys.update_last = c_tick
AND _upd_sys.update_state = 'PROCESSING'
FOR UPDATE OF e
FOR SHARE OF ep , p;
-- Process empires
FOR rec IN SELECT e.name_id AS id , ( v.status = 'PROCESSED' ) AS on_vacation ,
sum( p.population ) AS population
FROM sys.updates _upd_sys
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 verse.planets p ON p.name_id = ep.planet_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
WHERE _upd_sys.update_last = c_tick
AND _upd_sys.update_state = 'PROCESSING'
GROUP BY e.name_id , v.status
_points := emp.research_get_points( _empire , _on_vacation );
FOR _record IN
SELECT _emp_tech.technology_name_id , _emp_tech.emptech_points , _emp_tech.emptech_priority ,
_points * _weights.emptech_weight / ( _totals.emptech_total_weight * 1440 ) AS emptech_new_points ,
_def.technology_points::DOUBLE PRECISION AS technology_points
FROM emp.technologies_v2 _emp_tech
INNER JOIN emp.research_weights_view _weights
USING ( empire_id , technology_name_id )
INNER JOIN emp.research_total_weights_view _totals
USING ( empire_id )
INNER JOIN defs.technologies _def
USING ( technology_name_id )
WHERE _emp_tech.empire_id = _empire
AND _emp_tech.emptech_state = 'RESEARCH'
LOOP
-- Insert any missing tech line
INSERT INTO emp.technologies ( empire_id , line_id )
SELECT rec.id , l.name_id
FROM tech.lines l
LEFT OUTER JOIN emp.technologies t
ON t.line_id = l.name_id AND t.empire_id = rec.id
WHERE t.empire_id IS NULL;
-- Compute research output
r_points := rec.population * sys.get_constant( 'game.work.rpPerPopUnit' ) / 1440.0;
IF rec.on_vacation
THEN
r_points := r_points / sys.get_constant( 'vacation.researchDivider' );
IF _record.emptech_points + _record.emptech_new_points >= _record.technology_points THEN
UPDATE emp.technologies_v2
SET emptech_state = 'PENDING' ,
emptech_points = NULL ,
emptech_priority = NULL
WHERE technology_name_id = _record.technology_name_id
AND empire_id = _empire;
ELSE
UPDATE emp.technologies_v2
SET emptech_points = emptech_points + _record.emptech_new_points
WHERE technology_name_id = _record.technology_name_id
AND empire_id = _empire;
END IF;
-- Update technologies where:
-- 1) the level actually exists and
-- 2) accumulated points haven't reach the level's
FOR tu_rec IN SELECT t.line_id AS line_id , t.accumulated AS accumulated ,
l.points AS points , ( l.points - t.accumulated ) AS diff ,
l.id AS level_id
FROM emp.technologies t
INNER JOIN tech.levels l ON l.line_id = t.line_id
AND l.level = t.level AND t.accumulated < l.points
WHERE t.empire_id = rec.id
FOR UPDATE OF t
LOOP
UPDATE emp.technologies t SET accumulated = ( CASE
WHEN tu_rec.diff <= r_points THEN tu_rec.points
ELSE tu_rec.accumulated + r_points
END )
WHERE t.line_id = tu_rec.line_id AND t.empire_id = rec.id;
-- Send message
IF tu_rec.diff <= r_points
THEN
PERFORM events.tech_ready_event( rec.id , tu_rec.level_id );
END IF;
END LOOP;
END LOOP;
END;
$$ LANGUAGE plpgsql;
$gu_research_update_empire$;
REVOKE EXECUTE
ON FUNCTION sys.gu_research_update_empire( INT , BOOLEAN )
FROM PUBLIC;
/*
* Process a batch of empire research updates
* -------------------------------------------
*
* Update all empires in the batch which have both in-progress research and
* planets.
*
* Parameters:
* _tick The identifier of the current update cycle
*/
DROP FUNCTION IF EXISTS sys.process_empire_research_updates( BIGINT ) CASCADE;
CREATE FUNCTION sys.process_empire_research_updates( _tick BIGINT )
RETURNS VOID
LANGUAGE SQL
STRICT VOLATILE
SECURITY INVOKER
AS $process_empire_research_updates$
SELECT sys.gu_research_update_empire( empire_id , on_vacation )
FROM sys.gu_research_get_empires( $1::BIGINT );
$process_empire_research_updates$;
REVOKE EXECUTE
ON FUNCTION sys.process_empire_research_updates( BIGINT )
FROM PUBLIC;
SELECT sys.register_update_type( 'Empires' , 'EmpireResearch' ,
'Empire research points are being attributed to technologies.' ,