diff --git a/legacyworlds-server-beans-system/src/main/java/com/deepclone/lw/beans/sys/ConstantsRegistrarBean.java b/legacyworlds-server-beans-system/src/main/java/com/deepclone/lw/beans/sys/ConstantsRegistrarBean.java index 59743d9..38960a0 100644 --- a/legacyworlds-server-beans-system/src/main/java/com/deepclone/lw/beans/sys/ConstantsRegistrarBean.java +++ b/legacyworlds-server-beans-system/src/main/java/com/deepclone/lw/beans/sys/ConstantsRegistrarBean.java @@ -53,6 +53,14 @@ public class ConstantsRegistrarBean true ) ); cDesc = "Initial universe size (offset relative to the centre)."; defs.add( new ConstantDefinition( "game.universe.initialSize" , "Universe" , cDesc , 1.0 , 1.0 , true ) ); + + // Natural resources + cDesc = "Global recovery rate multiplier for natural resources. This should be kept close to 0."; + defs.add( new ConstantDefinition( "game.resources.recovery" , "Natural resources" , cDesc , 0.01 , 0.00001 , + 1.0 ) ); + cDesc = "Resource recovery dampener. Lower means less dampening"; + defs.add( new ConstantDefinition( "game.resources.recoveryDampening" , "Natural resources" , cDesc , 1.5 , 1.0 , + true ) ); // Happiness String[] hcNames = { diff --git a/legacyworlds-server-data/db-structure/parts/020-functions.sql b/legacyworlds-server-data/db-structure/parts/020-functions.sql index d97d3cd..18c48c0 100644 --- a/legacyworlds-server-data/db-structure/parts/020-functions.sql +++ b/legacyworlds-server-data/db-structure/parts/020-functions.sql @@ -24,6 +24,7 @@ \i parts/functions/110-prefs-functions.sql \i parts/functions/120-map-functions.sql \i parts/functions/140-planets-functions.sql +\i parts/functions/145-resource-providers-functions.sql \i parts/functions/150-battle-functions.sql \i parts/functions/160-battle-views.sql \i parts/functions/163-alliance-functions.sql diff --git a/legacyworlds-server-data/db-structure/parts/030-updates.sql b/legacyworlds-server-data/db-structure/parts/030-updates.sql index 47862f5..12efd35 100644 --- a/legacyworlds-server-data/db-structure/parts/030-updates.sql +++ b/legacyworlds-server-data/db-structure/parts/030-updates.sql @@ -19,4 +19,5 @@ \i parts/updates/080-planet-construction.sql \i parts/updates/090-planet-military.sql \i parts/updates/100-planet-population.sql +\i parts/updates/105-planet-resource-regeneration.sql \i parts/updates/110-planet-money.sql diff --git a/legacyworlds-server-data/db-structure/parts/data/000-typedefs.sql b/legacyworlds-server-data/db-structure/parts/data/000-typedefs.sql index f9fe790..edcef12 100644 --- a/legacyworlds-server-data/db-structure/parts/data/000-typedefs.sql +++ b/legacyworlds-server-data/db-structure/parts/data/000-typedefs.sql @@ -28,20 +28,53 @@ CREATE TYPE log_type -- Update types CREATE TYPE update_type AS ENUM ( + + /* Empires' money is being updated using the previous update's results */ 'EMPIRE_MONEY' , + + /* Empire research points are being attributed to technologies */ 'EMPIRE_RESEARCH' , + + /* The effects of empires' debts are being computed */ 'EMPIRE_DEBT' , + + /* Fleets which were 1 update away are arriving at their destination */ 'PLANET_FLEET_ARRIVALS' , + + /* Other fleets are moving */ 'PLANET_FLEET_MOVEMENTS' , + + /* Fleet states (e.g. "deploying", "unavailable", etc...) are being + * updated. + */ 'PLANET_FLEET_STATUS' , + + /* Start new battles where necessary */ 'PLANET_BATTLE_START' , + + /* Process all in-progress battles */ 'PLANET_BATTLE_MAIN' , + + /* Finalise battles that need to be ended */ 'PLANET_BATTLE_END' , + + /* Abandon planets */ 'PLANET_ABANDON' , + + /* Handle civilian build queues */ 'PLANET_CONSTRUCTION' , + + /* Handle military build queues */ 'PLANET_MILITARY' , + + /* Update planets' population */ 'PLANET_POPULATION' , - 'PLANET_MONEY' + + /* Regenerate resources in resource providers */ + 'PLANET_RES_REGEN' , + + /* Compute income and upkeep of planets */ + 'PLANET_MONEY' ); -- Types of recapitulative e-mail messages diff --git a/legacyworlds-server-data/db-structure/parts/functions/145-resource-providers-functions.sql b/legacyworlds-server-data/db-structure/parts/functions/145-resource-providers-functions.sql new file mode 100644 index 0000000..b79d4fd --- /dev/null +++ b/legacyworlds-server-data/db-structure/parts/functions/145-resource-providers-functions.sql @@ -0,0 +1,61 @@ +-- LegacyWorlds Beta 6 +-- PostgreSQL database scripts +-- +-- Resource providers functions +-- +-- Copyright(C) 2004-2012, DeepClone Development +-- -------------------------------------------------------- + + +/* + * Compute resource provider regeneration + * + * This function computes the quantity in a resource provider after it has + * been regenerated. + * + * Parameters: + * _quantity The current quantity of resources in the provider + * _max The maximal amount of resources supported by the + * provider + * _recovery_rate The provider's recovery rate + * + * Returns: + * ? The new quantity of resources. + */ +CREATE OR REPLACE FUNCTION verse.compute_provider_regeneration( + _quantity DOUBLE PRECISION , + _max DOUBLE PRECISION , + _recovery_rate DOUBLE PRECISION ) + RETURNS DOUBLE PRECISION + STRICT IMMUTABLE + SECURITY INVOKER + AS $compute_provider_regeneration$ + +DECLARE + _uc_recovery DOUBLE PRECISION; + _uc_dampening DOUBLE PRECISION; + _uc_ticks DOUBLE PRECISION; + _result DOUBLE PRECISION; + +BEGIN + _uc_recovery := sys.get_constant( 'game.resources.recovery' ); + _uc_dampening := sys.get_constant( 'game.resources.recoveryDampening' ); + _uc_ticks := 1440; -- FIXME: this should be a constant + + _result := ( 1 - _quantity / _max ) ^ _uc_dampening; + _result := _quantity + _result * _recovery_rate * _uc_recovery / _uc_ticks; + + IF _result > _max THEN + _result := _max; + END IF; + + RETURN _result; +END; +$compute_provider_regeneration$ LANGUAGE PLPGSQL; + + +REVOKE EXECUTE + ON FUNCTION verse.compute_provider_regeneration( + DOUBLE PRECISION , DOUBLE PRECISION , + DOUBLE PRECISION ) + FROM PUBLIC; diff --git a/legacyworlds-server-data/db-structure/parts/updates/105-planet-resource-regeneration.sql b/legacyworlds-server-data/db-structure/parts/updates/105-planet-resource-regeneration.sql new file mode 100644 index 0000000..9e59a4b --- /dev/null +++ b/legacyworlds-server-data/db-structure/parts/updates/105-planet-resource-regeneration.sql @@ -0,0 +1,47 @@ +-- LegacyWorlds Beta 6 +-- PostgreSQL database scripts +-- +-- Game updates - resource regeneration +-- +-- Copyright(C) 2004-2012, DeepClone Development +-- -------------------------------------------------------- + + +/* + * Resource provider regeneration update + * + * This function computes the regeneration of resource providers for the current batch + * of planets. + * + * Parameters: + * _tick The identifier of the game update + */ +CREATE OR REPLACE FUNCTION sys.process_planet_res_regen_updates( _tick BIGINT ) + RETURNS VOID + STRICT VOLATILE + SECURITY INVOKER +AS $process_planet_res_regen_updates$ + + UPDATE verse.resource_providers _provider + + SET resprov_quantity = verse.compute_provider_regeneration( + _provider.resprov_quantity , + _provider.resprov_quantity_max , + _provider.resprov_recovery + ) + + FROM sys.updates _upd_sys + INNER JOIN verse.updates _upd_verse + ON _upd_verse.update_id = _upd_sys.id + + WHERE _upd_sys.last_tick = $1 + AND _upd_sys.status = 'PROCESSING' + AND _upd_sys.gu_type = 'PLANET_RES_REGEN' + AND _provider.planet_id = _upd_verse.planet_id; + +$process_planet_res_regen_updates$ LANGUAGE SQL; + + +REVOKE EXECUTE + ON FUNCTION sys.process_planet_res_regen_updates( BIGINT ) + FROM PUBLIC; diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/verse/14500-compute-provider-regeneration.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/verse/14500-compute-provider-regeneration.sql new file mode 100644 index 0000000..f58a6c7 --- /dev/null +++ b/legacyworlds-server-data/db-structure/tests/admin/functions/verse/14500-compute-provider-regeneration.sql @@ -0,0 +1,204 @@ +/* + * Test the verse.compute_provider_regeneration() function + */ +BEGIN; + + /* Define the necessary constants using default values */ + SELECT sys.uoc_constant( 'game.resources.recovery' , '(test)' , 'Resources' , 0.01 ); + SELECT sys.uoc_constant( 'game.resources.recoveryDampening' , '(test)' , 'Resources' , 1.5 ); + + /* Make sure the functions are not immutable during the tests */ + ALTER FUNCTION sys.get_constant( TEXT ) VOLATILE; + ALTER FUNCTION verse.compute_provider_regeneration( DOUBLE PRECISION , DOUBLE PRECISION , DOUBLE PRECISION ) VOLATILE; + + /****** TESTS BEGIN HERE ******/ + SELECT plan( 6 ); + + + SELECT diag_test_name( 'verse.compute_provider_regeneration() - no regeneration at maximal quantity' ); + CREATE FUNCTION _run_tests( ) RETURNS BOOLEAN AS $$ + DECLARE + rr DOUBLE PRECISION; + BEGIN + rr := 0.05; + WHILE rr < 1 + LOOP + IF verse.compute_provider_regeneration( 100.0 , 100.0 , rr ) > 100.0 + THEN + RETURN FALSE; + END IF; + rr := rr + 0.05; + END LOOP; + RETURN TRUE; + END; + $$ LANGUAGE PLPGSQL; + SELECT ok( _run_tests() ); + DROP FUNCTION _run_tests( ); + + + SELECT diag_test_name( 'verse.compute_provider_regeneration() - regeneration >= 0' ); + CREATE FUNCTION _run_tests( ) RETURNS BOOLEAN AS $$ + DECLARE + qt DOUBLE PRECISION; + rr DOUBLE PRECISION; + BEGIN + qt := 0; + WHILE qt < 100.0 + LOOP + rr := 0.05; + WHILE rr < 1 + LOOP + IF verse.compute_provider_regeneration( qt , 100.0 , rr ) <= qt + THEN + RETURN FALSE; + END IF; + rr := rr + 0.05; + END LOOP; + qt := qt + 5; + END LOOP; + RETURN TRUE; + END; + $$ LANGUAGE PLPGSQL; + SELECT ok( _run_tests() ); + DROP FUNCTION _run_tests( ); + + + SELECT diag_test_name( 'verse.compute_provider_regeneration() - higher quantity => slower regeneration' ); + CREATE FUNCTION _run_tests( ) RETURNS BOOLEAN AS $$ + DECLARE + pdiff DOUBLE PRECISION; + diff DOUBLE PRECISION; + qt DOUBLE PRECISION; + rr DOUBLE PRECISION; + BEGIN + rr := 0.05; + WHILE rr < 1 + LOOP + qt := 0; + WHILE qt < 100.0 + LOOP + diff := verse.compute_provider_regeneration( qt , 100.0 , rr ) - qt; + IF qt <> 0 AND diff >= pdiff + THEN + RETURN FALSE; + END IF; + pdiff := diff; + qt := qt + 5; + END LOOP; + rr := rr + 0.05; + END LOOP; + RETURN TRUE; + END; + $$ LANGUAGE PLPGSQL; + SELECT ok( _run_tests() ); + DROP FUNCTION _run_tests( ); + + + SELECT diag_test_name( 'verse.compute_provider_regeneration() - higher recovery rate => faster regeneration' ); + CREATE FUNCTION _run_tests( ) RETURNS BOOLEAN AS $$ + DECLARE + pdiff DOUBLE PRECISION; + diff DOUBLE PRECISION; + qt DOUBLE PRECISION; + rr DOUBLE PRECISION; + BEGIN + qt := 0; + WHILE qt < 100.0 + LOOP + rr := 0.05; + WHILE rr < 1 + LOOP + diff := verse.compute_provider_regeneration( qt , 100.0 , rr ) - qt; + IF rr > 0.06 AND diff <= pdiff + THEN + RETURN FALSE; + END IF; + pdiff := diff; + rr := rr + 0.05; + END LOOP; + qt := qt + 5; + END LOOP; + RETURN TRUE; + END; + $$ LANGUAGE PLPGSQL; + SELECT ok( _run_tests() ); + DROP FUNCTION _run_tests( ); + + + SELECT diag_test_name( 'verse.compute_provider_regeneration() - effect of game.resources.recovery' ); + CREATE FUNCTION _run_tests( ) RETURNS BOOLEAN AS $$ + DECLARE + pdiff DOUBLE PRECISION; + diff DOUBLE PRECISION; + qt DOUBLE PRECISION; + rr DOUBLE PRECISION; + BEGIN + qt := 0; + WHILE qt < 100.0 + LOOP + rr := 0.01; + WHILE rr < 1 + LOOP + UPDATE sys.constant_definitions + SET c_value = rr + WHERE name = 'game.resources.recovery'; + diff := verse.compute_provider_regeneration( qt , 100.0 , 0.5 ) - qt; + IF rr > 0.011 AND diff <= pdiff + THEN + RETURN FALSE; + END IF; + pdiff := diff; + rr := rr + 0.01; + END LOOP; + qt := qt + 5; + END LOOP; + RETURN TRUE; + END; + $$ LANGUAGE PLPGSQL; + SELECT ok( _run_tests() ); + DROP FUNCTION _run_tests( ); + UPDATE sys.constant_definitions + SET c_value = 0.01 + WHERE name = 'game.resources.recovery'; + + + + SELECT diag_test_name( 'verse.compute_provider_regeneration() - effect of game.resources.recoveryDampening' ); + CREATE FUNCTION _run_tests( ) RETURNS BOOLEAN AS $$ + DECLARE + pdiff DOUBLE PRECISION; + diff DOUBLE PRECISION; + qt DOUBLE PRECISION; + rrd DOUBLE PRECISION; + BEGIN + qt := 5; + WHILE qt < 100.0 + LOOP + rrd := 1; + WHILE rrd < 3 + LOOP + UPDATE sys.constant_definitions + SET c_value = rrd + WHERE name = 'game.resources.recoveryDampening'; + diff := verse.compute_provider_regeneration( qt , 100.0 , 0.5 ) - qt; + IF rrd > 1.01 AND diff >= pdiff + THEN + RETURN FALSE; + END IF; + pdiff := diff; + rrd := rrd + 0.25; + END LOOP; + qt := qt + 5; + END LOOP; + RETURN TRUE; + END; + $$ LANGUAGE PLPGSQL; + SELECT ok( _run_tests() ); + DROP FUNCTION _run_tests( ); + UPDATE sys.constant_definitions + SET c_value = 1.5 + WHERE name = 'game.resources.recoveryDampening'; + + + SELECT * FROM finish( ); +ROLLBACK; \ No newline at end of file diff --git a/legacyworlds-server-data/db-structure/tests/admin/updates/10500-process-planet-res-regen-updates.sql b/legacyworlds-server-data/db-structure/tests/admin/updates/10500-process-planet-res-regen-updates.sql new file mode 100644 index 0000000..54385b4 --- /dev/null +++ b/legacyworlds-server-data/db-structure/tests/admin/updates/10500-process-planet-res-regen-updates.sql @@ -0,0 +1,87 @@ +/* + * Test for the sys.process_planet_res_regen_updates() function + */ +BEGIN; + + /* We need to create a natural resource and a pair of planets */ + \i utils/strings.sql + \i utils/resources.sql + \i utils/accounts.sql + \i utils/naming.sql + \i utils/universe.sql + SELECT _create_natural_resources( 1 , 'testResource' ); + SELECT _create_raw_planets( 3 , 'testPlanet' ); + + /* Define the necessary constants using default values */ + SELECT sys.uoc_constant( 'game.resources.recovery' , '(test)' , 'Resources' , 0.01 ); + SELECT sys.uoc_constant( 'game.resources.recoveryDampening' , '(test)' , 'Resources' , 1.5 ); + + /* Create resource providers */ + INSERT INTO verse.resource_providers ( planet_id , resource_name_id , + resprov_quantity_max , resprov_quantity , + resprov_difficulty , resprov_recovery ) + VALUES ( + _get_map_name( 'testPlanet1' ) , _get_string( 'testResource1' ) , + 100 , 50 , + 0.5 , 0.5 + ) , ( + _get_map_name( 'testPlanet2' ) , _get_string( 'testResource1' ) , + 100 , 100 , + 0.5 , 0.5 + ) , ( + _get_map_name( 'testPlanet3' ) , _get_string( 'testResource1' ) , + 100 , 50 , + 0.5 , 0.5 + ); + + /* Insert update records for the planets. Planets 1 and 2 will be processed, + * planet testPlanet3 will not. + */ + INSERT INTO sys.updates ( id , gu_type , status , last_tick ) + VALUES ( 1 , 'PLANET_RES_REGEN' , 'PROCESSING' , 0 ); + INSERT INTO verse.updates ( update_id , planet_id ) + VALUES ( 1 , _get_map_name( 'testPlanet1' ) ); + + INSERT INTO sys.updates ( id , gu_type , status , last_tick ) + VALUES ( 2 , 'PLANET_RES_REGEN' , 'PROCESSING' , 0 ); + INSERT INTO verse.updates ( update_id , planet_id ) + VALUES ( 2 , _get_map_name( 'testPlanet2' ) ); + + 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 */ + CREATE FUNCTION _get_quantity( _planet TEXT ) + RETURNS DOUBLE PRECISION + AS $$ + SELECT resprov_quantity FROM verse.resource_providers + WHERE planet_id = _get_map_name( $1 ) + AND resource_name_id = _get_string( 'testResource1' ); + $$ LANGUAGE SQL; + + /****** TESTS BEGIN HERE ******/ + SELECT plan( 6 ); + + + SELECT sys.process_planet_res_regen_updates( 10 ); + SELECT diag_test_name( 'PLANET_RES_REGEN update - Wrong update identifier (1/3)' ); + SELECT ok( _get_quantity( 'testPlanet1' ) = 50 ); + SELECT diag_test_name( 'PLANET_RES_REGEN update - Wrong update identifier (2/3)' ); + SELECT ok( _get_quantity( 'testPlanet2' ) = 100 ); + SELECT diag_test_name( 'PLANET_RES_REGEN update - Wrong update identifier (3/3)' ); + SELECT ok( _get_quantity( 'testPlanet3' ) = 50 ); + + + SELECT sys.process_planet_res_regen_updates( 0 ); + SELECT diag_test_name( 'PLANET_RES_REGEN update - Processed planet with quantity < max.' ); + SELECT ok( _get_quantity( 'testPlanet1' ) > 50 ); + SELECT diag_test_name( 'PLANET_RES_REGEN update - Processed planet with quantity = max.' ); + SELECT ok( _get_quantity( 'testPlanet2' ) = 100 ); + SELECT diag_test_name( 'PLANET_RES_REGEN update - Already processed planet' ); + SELECT ok( _get_quantity( 'testPlanet3' ) = 50 ); + + + SELECT * FROM finish( ); +ROLLBACK; \ No newline at end of file