diff --git a/legacyworlds-server-data/db-structure/parts/020-functions.sql b/legacyworlds-server-data/db-structure/parts/020-functions.sql
index 18c48c0..027e727 100644
--- a/legacyworlds-server-data/db-structure/parts/020-functions.sql
+++ b/legacyworlds-server-data/db-structure/parts/020-functions.sql
@@ -16,6 +16,8 @@
 \i parts/functions/035-users-view.sql
 \i parts/functions/040-empire-functions.sql
 \i parts/functions/050-computation-functions.sql
+\i parts/functions/053-generator-basics.sql
+\i parts/functions/055-generator-resources.sql
 \i parts/functions/060-universe-functions.sql
 \i parts/functions/070-users-functions.sql
 \i parts/functions/075-session-functions.sql
diff --git a/legacyworlds-server-data/db-structure/parts/functions/050-computation-functions.sql b/legacyworlds-server-data/db-structure/parts/functions/050-computation-functions.sql
index e5304c2..ebc8ed6 100644
--- a/legacyworlds-server-data/db-structure/parts/functions/050-computation-functions.sql
+++ b/legacyworlds-server-data/db-structure/parts/functions/050-computation-functions.sql
@@ -7,6 +7,115 @@
 -- --------------------------------------------------------
 
 
+/*
+ * Random value with deviation
+ * 
+ * Parameters:
+ *		_mean		the mean value
+ *		_deviation	the deviation
+ *
+ * Returns:
+ *		?			a random value between _mean - _deviation and
+ *						_mean + _deviation, with a higher probability
+ *						of a value that is close to _mean
+ */
+DROP FUNCTION IF EXISTS verse.random_deviation( DOUBLE PRECISION , DOUBLE PRECISION );
+CREATE FUNCTION verse.random_deviation( _mean DOUBLE PRECISION , _deviation DOUBLE PRECISION )
+		RETURNS DOUBLE PRECISION
+		STRICT VOLATILE
+		SECURITY INVOKER
+	AS $random_deviation$
+
+DECLARE
+	_result	DOUBLE PRECISION;
+
+BEGIN
+	_result := _deviation * RANDOM( ) ^ 2.5;
+	IF RANDOM() < 0.5 THEN
+		_result := -_result;
+	END IF;
+	RETURN _result + _mean;
+END;
+$random_deviation$ LANGUAGE PLPGSQL;
+
+
+REVOKE EXECUTE
+	ON FUNCTION verse.random_deviation( DOUBLE PRECISION , DOUBLE PRECISION )
+	FROM PUBLIC;
+
+
+
+/*
+ * Randomly distribute some part of a total value
+ * 
+ * This function can be used when a total value must be distributed between
+ * various items. It will compute the minimal and maximal values that may be
+ * attributed, enforcing the fact that the whole value needs to be consumed
+ * in the end, and that values must conform to a specific range expressed as
+ * a mean value and a deviation.
+ * 
+ * The total value is assumed to be valid with regards to the mean and
+ * deviation. That is:
+ * 
+ *		_parts * ( _mean - _deviation ) <= _quantity
+ *		_parts * ( _mean + _deviation ) >= _quantity
+ *
+ * Parameters:
+ *		_quantity		the total quantity left to distribute
+ *		_parts			the amount of items left
+ *		_mean			the result's mean value
+ *		_deviation		the result's deviation
+ *
+ * Returns:
+ * 		?				the value to attribute to the nex item
+ */
+DROP FUNCTION IF EXISTS verse.get_random_part( DOUBLE PRECISION , INT , DOUBLE PRECISION , DOUBLE PRECISION );
+CREATE FUNCTION verse.get_random_part(
+			_quantity DOUBLE PRECISION ,
+			_parts INT ,
+			_mean DOUBLE PRECISION ,
+			_deviation DOUBLE PRECISION )
+		RETURNS DOUBLE PRECISION
+		STRICT VOLATILE
+		SECURITY INVOKER
+	AS $get_random_part$
+
+DECLARE
+	_min	DOUBLE PRECISION;
+	_max	DOUBLE PRECISION;
+	_n_mean	DOUBLE PRECISION;
+
+BEGIN
+	IF _parts = 1 THEN
+		RETURN _quantity;
+	END IF;
+
+	_min := _quantity - ( _mean + _deviation ) * ( _parts - 1 );
+	IF _min < _mean - _deviation THEN
+		_min := _mean - _deviation;
+	END IF;
+
+	_max := _quantity - ( _mean - _deviation ) * ( _parts - 1 );
+	IF _max > _mean + _deviation THEN
+		_max := _mean + _deviation;
+	END IF;
+	
+	IF _min = _max THEN
+		RETURN _min;
+	END IF;
+	
+	_n_mean := ( _min + _max ) * 0.5;
+	RETURN verse.random_deviation( _n_mean , _n_mean - _min );
+END;
+$get_random_part$ LANGUAGE PLPGSQL;
+
+REVOKE EXECUTE
+	ON FUNCTION verse.get_random_part( DOUBLE PRECISION , INT , DOUBLE PRECISION , DOUBLE PRECISION )
+	FROM PUBLIC;
+
+
+
+
 --
 -- sigma( x ) = exp( x ) / ( 1 + exp( x ) )
 --
diff --git a/legacyworlds-server-data/db-structure/parts/functions/053-generator-basics.sql b/legacyworlds-server-data/db-structure/parts/functions/053-generator-basics.sql
new file mode 100644
index 0000000..a7fb6f0
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/parts/functions/053-generator-basics.sql
@@ -0,0 +1,51 @@
+-- LegacyWorlds Beta 6
+-- PostgreSQL database scripts
+--
+-- A few types and internal functions used in most parts
+-- of the universe generator.
+--
+-- Copyright(C) 2004-2012, DeepClone Development
+-- --------------------------------------------------------
+
+
+/* The coordinates of the area being generated. */
+DROP TYPE IF EXISTS verse.generator_area_type CASCADE;
+CREATE TYPE verse.generator_area_type AS (
+	x0 INT , y0 INT ,
+	x1 INT , y1 INT
+);
+
+
+
+/*
+ * List some quantity of random planets from an area of the universe
+ * 
+ * This function returns a set of planet identifiers chosen at random from the
+ * specified area of the universe.
+ * 
+ * Parameters:
+ *		_area		The area to select planets from
+ *		_count		The maximal amount of planets to return
+ */
+DROP FUNCTION IF EXISTS verse.list_random_planets_in( verse.generator_area_type , INT );
+CREATE FUNCTION verse.list_random_planets_in( _area verse.generator_area_type , _count INT )
+	RETURNS SETOF INT
+	STRICT VOLATILE
+	SECURITY INVOKER
+AS $list_random_planets_in$
+
+	SELECT _planets.name_id
+		FROM verse.planets _planets
+			INNER JOIN verse.systems _systems
+				ON _planets.system_id = _systems.id
+		WHERE _systems.x BETWEEN $1.x0 AND $1.x1
+			AND _systems.y BETWEEN $1.y0 AND $1.y1
+		ORDER BY RANDOM( )
+		LIMIT $2;
+
+$list_random_planets_in$ LANGUAGE SQL;
+
+REVOKE EXECUTE
+	ON FUNCTION verse.list_random_planets_in( verse.generator_area_type ,
+			INT )
+	FROM PUBLIC;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/parts/functions/055-generator-resources.sql b/legacyworlds-server-data/db-structure/parts/functions/055-generator-resources.sql
new file mode 100644
index 0000000..a2418ac
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/parts/functions/055-generator-resources.sql
@@ -0,0 +1,384 @@
+-- LegacyWorlds Beta 6
+-- PostgreSQL database scripts
+--
+-- Functions for the resource provider generator
+--
+-- Copyright(C) 2004-2012, DeepClone Development
+-- --------------------------------------------------------
+
+
+
+/*
+ * Resource provider generator data
+ * 
+ * This data type is used to store statistics about the existing resource
+ * providers. A single item of the type represents both statistics and
+ * parameters for a given resource type.
+ */
+DROP TYPE IF EXISTS verse.resprov_generator_type CASCADE;
+CREATE TYPE verse.resprov_generator_type AS (
+		/* Type of natural resource */
+	 	resource_name_id		INT ,
+
+	 	/* Planets in the universe */
+	 	planets					DOUBLE PRECISION ,
+	 	
+	 	/* Providers of this type in the universe */
+	 	providers				DOUBLE PRECISION ,
+
+	 	/* Presence probability (from the resource's definition) */
+	 	presence				DOUBLE PRECISION ,
+
+	 	/* Total maximal quantity of this type of resource in the whole
+	 	 * universe.
+	 	 */
+	 	quantity				DOUBLE PRECISION ,
+	 	
+	 	/* Average quantity (from the resource's definition) */
+	 	quantity_avg			DOUBLE PRECISION ,
+	 	
+	 	/* Maximal deviation from the average quantity (from the resource's
+	 	 * definition)
+	 	 */
+	 	quantity_dev			DOUBLE PRECISION ,
+
+	 	/* Total extraction difficulty for this type of resource in the whole
+	 	 * universe.
+	 	 */
+	 	difficulty				DOUBLE PRECISION ,
+	 	
+	 	/* Average difficulty (from the resource's definition) */
+	 	difficulty_avg			DOUBLE PRECISION ,
+	 	
+	 	/* Maximal deviation from the average difficulty (from the resource's
+	 	 * definition)
+	 	 */
+	 	difficulty_dev			DOUBLE PRECISION ,
+
+	 	/* Total recovery rate for this type of resource in the whole
+	 	 * universe.
+	 	 */
+	 	recovery				DOUBLE PRECISION ,
+	 	
+	 	/* Average recovery rate (from the resource's definition) */
+	 	recovery_avg			DOUBLE PRECISION ,
+	 	
+	 	/* Maximal deviation from the average recovery rate (from the
+	 	 */
+	 	recovery_dev			DOUBLE PRECISION
+);
+
+
+
+/*
+ * Collect resource provider statistics
+ * 
+ * This procedure collects statistics about resource providers into a
+ * temporary table named resource_statistics, using the resprov_generator_type
+ * as the table's structure. The table will be dropped on commit.
+ * 
+ * This function is necessary because the statistics must be collected before
+ * new planets are generated.
+ */
+DROP FUNCTION IF EXISTS verse.collect_resprov_statistics( );
+CREATE FUNCTION verse.collect_resprov_statistics( )
+		RETURNS VOID
+		STRICT VOLATILE
+		SECURITY INVOKER
+	AS $collect_resprov_statistics$
+
+BEGIN
+	 CREATE TEMP TABLE rp_stats
+	 	OF verse.resprov_generator_type
+	 	ON COMMIT DROP;
+
+	INSERT INTO rp_stats
+		SELECT resource_name_id ,
+					_pcount.planets AS planets ,
+					( CASE
+						WHEN _rp_stats.providers IS NULL THEN 0
+						ELSE _rp_stats.providers
+					END ) AS providers ,
+					natres_p_presence AS presence ,
+					( CASE
+						WHEN _rp_stats.tot_quantity IS NULL THEN 0
+						ELSE _rp_stats.tot_quantity
+					END ) AS quantity ,
+					natres_quantity_avg AS quantity_avg ,
+					natres_quantity_dev AS quantity_dev ,
+					( CASE
+						WHEN _rp_stats.tot_difficulty IS NULL THEN 0
+						ELSE _rp_stats.tot_difficulty
+					END ) AS difficulty ,
+					natres_difficulty_avg AS difficulty_avg ,
+					natres_difficulty_dev AS difficulty_dev ,
+					( CASE
+						WHEN _rp_stats.tot_recovery IS NULL THEN 0
+						ELSE _rp_stats.tot_recovery
+					END ) AS recovery ,
+					natres_recovery_avg AS recovery_avg ,
+					natres_recovery_dev AS recovery_dev
+			FROM defs.natural_resources
+				LEFT OUTER JOIN (
+					SELECT resource_name_id ,
+							COUNT(*) AS providers ,
+							SUM( resprov_quantity_max ) AS tot_quantity ,
+							SUM( resprov_difficulty ) AS tot_difficulty ,
+							SUM( resprov_recovery ) AS tot_recovery
+						FROM verse.resource_providers
+						GROUP BY resource_name_id
+					) AS _rp_stats USING ( resource_name_id )
+				CROSS JOIN (
+					SELECT COUNT(*) AS planets
+						FROM verse.planets
+				) AS _pcount;
+END;
+$collect_resprov_statistics$ LANGUAGE PLPGSQL;
+
+REVOKE EXECUTE
+	ON FUNCTION verse.collect_resprov_statistics( )
+	FROM PUBLIC;
+
+
+
+/* Compute a random delta for one of the resource provider parameters
+ * 
+ * This function computes the total change on one of the resource provider
+ * parameters. The resulting value can then be split amongst resource
+ * providers as they are created.
+ * 
+ * Parameters:
+ *		_existing			Amount of existing resource providers
+ *		_new				Amount of resource providers being created
+ *		_total				Current total value for the parameter
+ *		_p_average			Average parameter value (from the definition)
+ *		_p_deviation		Parameter value deviation (from the definition)
+ *
+ * Returns:
+ *		?					The total value to distribute amongst new resource
+ *								providers
+ */
+DROP FUNCTION IF EXISTS verse.compute_rpp_delta( DOUBLE PRECISION , DOUBLE PRECISION ,
+		DOUBLE PRECISION , DOUBLE PRECISION , DOUBLE PRECISION );
+CREATE FUNCTION verse.compute_rpp_delta(
+			_existing		DOUBLE PRECISION ,
+			_new			DOUBLE PRECISION ,
+			_total			DOUBLE PRECISION ,
+			_p_average		DOUBLE PRECISION ,
+			_p_deviation	DOUBLE PRECISION )
+		RETURNS DOUBLE PRECISION
+		STRICT VOLATILE
+		SECURITY INVOKER
+	AS $compute_rpp_delta$
+
+DECLARE
+	_result	DOUBLE PRECISION;
+
+BEGIN
+	_result := verse.random_deviation( _p_average , _p_deviation )
+					* ( _existing + _new ) - _total;
+
+	IF _result < ( _p_average - _p_deviation ) * _new THEN
+		_result := ( _p_average - _p_deviation ) * _new;
+	ELSIF _result > ( _p_average + _p_deviation ) * _new THEN
+		_result := ( _p_average + _p_deviation ) * _new;
+	END IF;
+	
+	RETURN _result;
+END;
+$compute_rpp_delta$ LANGUAGE PLPGSQL;
+
+REVOKE EXECUTE
+	ON FUNCTION verse.compute_rpp_delta( DOUBLE PRECISION , DOUBLE PRECISION ,
+			DOUBLE PRECISION , DOUBLE PRECISION , DOUBLE PRECISION )
+	FROM PUBLIC;
+
+
+
+/*
+ * Create a single resource provider
+ * 
+ * This function creates a single resource provider on some planet. It will
+ * return the updated values for the amount of providers left to handle and
+ * the totals.
+ * 
+ * Parameters:
+ *		_planet			The identifier of the planet to create a provider on
+ *		_data			The statistics and parameters for the type of resource
+ *		_providers_left	The amount of resource providers that still need to be
+ *							generated (including the current provider)
+ *		_tot_quantity	The total value left to distribute for the providers'
+ *							maximal quantity
+ *		_tot_difficulty	The total value left to distribute for the providers'
+ *							extraction difficulty
+ *		_tot_recovery	The total value left to distribute for the providers'
+ *							recovery rate
+ *
+ * Returns:
+ *		_providers_left	The updated value for the amount of providers left
+ *		_tot_quantity	The updated value for the total maximal quantity to
+ *							distribute
+ *		_tot_difficulty	The updated value for the total extraction difficulty
+ *							to distribute
+ *		_tot_recovery	The updated value for the total recovery rate to
+ *							distribute
+ */
+DROP FUNCTION IF EXISTS verse.create_resource_provider(
+	INT , verse.resprov_generator_type , INT , DOUBLE PRECISION ,
+	DOUBLE PRECISION , DOUBLE PRECISION );
+CREATE FUNCTION verse.create_resource_provider(
+			_planet					INT ,
+			_data					verse.resprov_generator_type ,
+			INOUT _providers_left	INT ,
+			INOUT _tot_quantity		DOUBLE PRECISION ,
+			INOUT _tot_difficulty	DOUBLE PRECISION ,
+			INOUT _tot_recovery		DOUBLE PRECISION )
+		STRICT VOLATILE
+		SECURITY INVOKER
+	AS $create_resource_provider$
+
+DECLARE
+	_quantity		DOUBLE PRECISION;
+	_difficulty		DOUBLE PRECISION;
+	_recovery		DOUBLE PRECISION;
+
+BEGIN
+	_quantity := verse.get_random_part( _tot_quantity , _providers_left ,
+		_data.quantity_avg , _data.quantity_dev );
+	_difficulty := verse.get_random_part( _tot_difficulty , _providers_left ,
+		_data.difficulty_avg , _data.difficulty_dev );
+	_recovery := verse.get_random_part( _tot_recovery , _providers_left ,
+		_data.recovery_avg , _data.recovery_dev );
+
+	RAISE NOTICE 'Resource #% planet #%: quantity: %   difficulty: %   recovery: %',
+		_data.resource_name_id , _planet ,
+		_quantity , _difficulty , _recovery;
+
+	INSERT INTO verse.resource_providers (
+			planet_id , resource_name_id , resprov_quantity_max ,
+			resprov_quantity , resprov_difficulty , resprov_recovery
+		) VALUES (
+			_planet , _data.resource_name_id , _quantity ,
+			_quantity , _difficulty , _recovery
+		);
+
+	_tot_quantity := _tot_quantity - _quantity;
+	_tot_difficulty := _tot_difficulty - _difficulty;
+	_tot_recovery := _tot_recovery - _recovery;
+	_providers_left := _providers_left - 1;
+END;
+$create_resource_provider$ LANGUAGE PLPGSQL;
+
+REVOKE EXECUTE
+	ON FUNCTION verse.create_resource_provider( INT ,
+			verse.resprov_generator_type , INT , DOUBLE PRECISION ,
+			DOUBLE PRECISION , DOUBLE PRECISION )
+	FROM PUBLIC;
+
+
+
+/*
+ * Create resource providers for a given type of resource
+ * 
+ * This function will create resource providers for some specified type of
+ * resource in an area of the universe. It tries to balance the generated
+ * values according to the resource's definition.
+ * 
+ * Parameters:
+ *		_area		The area to generate resource providers in
+ *		_data		The identifier, definition and statistics for the type of
+ *						resource
+ */
+DROP FUNCTION IF EXISTS verse.create_resource_providers(
+	verse.generator_area_type , verse.resprov_generator_type );
+CREATE FUNCTION verse.create_resource_providers(
+			_area verse.generator_area_type ,
+			_data verse.resprov_generator_type )
+		RETURNS VOID
+		STRICT VOLATILE
+		SECURITY INVOKER
+	AS $create_resource_providers$
+
+DECLARE
+	_ncount			INT;
+	_create			INT;
+	_tot_quantity	DOUBLE PRECISION;
+	_tot_difficulty	DOUBLE PRECISION;
+	_tot_recovery	DOUBLE PRECISION;
+	_planet			INT;
+
+BEGIN
+	_ncount := ( _area.x1 - _area.x0 + 1 ) * ( _area.y1 - _area.y0 + 1 ) * 5;
+
+	-- Determine the amount of providers to create
+	_create := FLOOR( ( _data.planets + _ncount ) * _data.presence - _data.providers )::INT;
+	IF _create <= 0 THEN
+		RETURN;
+	ELSIF _create > _ncount THEN
+		_create := _ncount;
+	END IF;
+
+	-- Compute the total delta for quantity, difficulty and recovery rate
+	_tot_quantity := verse.compute_rpp_delta( _data.providers , _create ,
+			_data.quantity , _data.quantity_avg , _data.quantity_dev );
+	_tot_difficulty := verse.compute_rpp_delta( _data.providers , _create ,
+			_data.difficulty , _data.difficulty_avg , _data.difficulty_dev );
+	_tot_recovery := verse.compute_rpp_delta( _data.providers , _create ,
+			_data.recovery , _data.recovery_avg , _data.recovery_dev );
+
+	RAISE NOTICE 'Resource #%: % new provider(s), quantity: % (avg. %) , difficulty: % (avg. %), recovery: % (avg. %)',
+		_data.resource_name_id , _create ,
+		_tot_quantity , _tot_quantity / _create ,
+		_tot_difficulty , _tot_difficulty / _create ,
+		_tot_recovery , _tot_recovery / _create;
+
+	-- Select random planets to add resource providers to
+	FOR _planet IN SELECT * FROM verse.list_random_planets_in( _area , _create )
+	LOOP
+		SELECT INTO _create , _tot_quantity , _tot_difficulty , _tot_recovery
+			* FROM verse.create_resource_provider( _planet , _data , _create ,
+					_tot_quantity , _tot_difficulty , _tot_recovery );
+	END LOOP;
+
+END;
+$create_resource_providers$ LANGUAGE PLPGSQL;
+
+REVOKE EXECUTE
+	ON FUNCTION verse.create_resource_providers( verse.generator_area_type ,
+			verse.resprov_generator_type )
+	FROM PUBLIC;
+
+
+
+/*
+ * Create resource providers in some area of the universe
+ * 
+ * This function creates resource providers in the specified area using the
+ * statistics collected before the area was created to balance the resource
+ * providers' parameters.
+ * 
+ * Parameters:
+ *		_area		The area to generate resource providers in
+ */
+DROP FUNCTION IF EXISTS verse.create_resource_providers( verse.generator_area_type );
+CREATE FUNCTION verse.create_resource_providers( _area verse.generator_area_type )
+		RETURNS VOID
+		STRICT VOLATILE
+		SECURITY INVOKER
+	AS $create_resource_providers$
+
+DECLARE
+	_rp_data		verse.resprov_generator_type;
+
+BEGIN
+	FOR _rp_data IN SELECT * FROM rp_stats
+	LOOP
+		PERFORM verse.create_resource_providers( _area , _rp_data );
+	END LOOP;
+END;
+$create_resource_providers$ LANGUAGE PLPGSQL;
+
+
+REVOKE EXECUTE
+	ON FUNCTION verse.create_resource_providers( verse.generator_area_type )
+	FROM PUBLIC;
diff --git a/legacyworlds-server-data/db-structure/parts/functions/060-universe-functions.sql b/legacyworlds-server-data/db-structure/parts/functions/060-universe-functions.sql
index 780986a..1a135ea 100644
--- a/legacyworlds-server-data/db-structure/parts/functions/060-universe-functions.sql
+++ b/legacyworlds-server-data/db-structure/parts/functions/060-universe-functions.sql
@@ -245,11 +245,12 @@ $$ LANGUAGE plpgsql;
 -- Generate multiple systems at the specified coordinates
 --
 -- Parameters:
---	(x0,y0)-(x1,y1)		Area to generate
+--	_area				Area to generate
 --	ipop				Initial population
 --
 
-CREATE OR REPLACE FUNCTION verse.create_systems( x0 INT , y0 INT , x1 INT , y1 INT , ipop REAL )
+DROP FUNCTION IF EXISTS verse.create_systems( verse.generator_area_type , REAL );
+CREATE FUNCTION verse.create_systems( _area verse.generator_area_type , ipop REAL )
 		RETURNS VOID
 		STRICT VOLATILE
 		SECURITY INVOKER
@@ -259,18 +260,27 @@ DECLARE
 	y		INT;
 	npics	INT;
 BEGIN
+	PERFORM verse.collect_resource_statistics( );
+
 	npics := floor( sys.get_constant( 'game.universe.pictures' ) );
-	FOR x IN x0 .. x1
+	FOR x IN _area.x0 .. area.x1
 	LOOP
-		FOR y IN y0 .. y1
+		FOR y IN area.y0 .. area.y1
 		LOOP
 			PERFORM verse.create_system( x , y , ipop , npics );
 		END LOOP;
 	END LOOP;
+
+	PERFORM verse.create_resource_providers( _area );
 END;
 $$ LANGUAGE plpgsql;
 
 
+REVOKE EXECUTE
+	ON FUNCTION verse.create_systems( _area verse.generator_area_type , REAL )
+	FROM PUBLIC;
+
+
 
 --
 -- Generate the initial universe
@@ -348,7 +358,7 @@ BEGIN
 
 	-- Get average population and generate new systems
 	SELECT INTO pop AVG( population ) FROM verse.planets;
-	PERFORM verse.create_systems( x0 , y0 , x1 , y1 , pop );
+	PERFORM verse.create_systems( ROW( x0 , y0 , x1 , y1 ) , pop );
 END;
 $$ LANGUAGE plpgsql;
 
@@ -391,4 +401,4 @@ BEGIN
 END;
 $$ LANGUAGE plpgsql;
 
-GRANT EXECUTE ON FUNCTION verse.generate() TO :dbuser;
\ No newline at end of file
+GRANT EXECUTE ON FUNCTION verse.generate() TO :dbuser;
diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/defs/02500-uoc-resource-internal.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/025-resources/010-uoc-resource-internal.sql
similarity index 100%
rename from legacyworlds-server-data/db-structure/tests/admin/functions/defs/02500-uoc-resource-internal.sql
rename to legacyworlds-server-data/db-structure/tests/admin/functions/025-resources/010-uoc-resource-internal.sql
diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/defs/02501-uoc-resource.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/025-resources/020-uoc-resource.sql
similarity index 100%
rename from legacyworlds-server-data/db-structure/tests/admin/functions/defs/02501-uoc-resource.sql
rename to legacyworlds-server-data/db-structure/tests/admin/functions/025-resources/020-uoc-resource.sql
diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/defs/02502-uoc-natres-internal.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/025-resources/030-uoc-natres-internal.sql
similarity index 100%
rename from legacyworlds-server-data/db-structure/tests/admin/functions/defs/02502-uoc-natres-internal.sql
rename to legacyworlds-server-data/db-structure/tests/admin/functions/025-resources/030-uoc-natres-internal.sql
diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/defs/02503-uoc-natural-resource.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/025-resources/040-uoc-natural-resource.sql
similarity index 100%
rename from legacyworlds-server-data/db-structure/tests/admin/functions/defs/02503-uoc-natural-resource.sql
rename to legacyworlds-server-data/db-structure/tests/admin/functions/025-resources/040-uoc-natural-resource.sql
diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/050-computation/010-get-random-part.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/050-computation/010-get-random-part.sql
new file mode 100644
index 0000000..7192fee
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/admin/functions/050-computation/010-get-random-part.sql
@@ -0,0 +1,44 @@
+/*
+ * Test the verse.get_random_part() function
+ */
+BEGIN;
+	SELECT plan( 6 );
+
+	/* First set of tests: results of the function with a fake
+	 * verse.random_deviation() that always returns the minimal possible
+	 * value.
+	 */
+	CREATE OR REPLACE FUNCTION verse.random_deviation( _mean DOUBLE PRECISION , _deviation DOUBLE PRECISION )
+		RETURNS DOUBLE PRECISION
+		STRICT VOLATILE
+	AS $$
+		SELECT $1 - $2;
+	$$ LANGUAGE SQL;
+
+	SELECT diag_test_name( 'verse.get_random_part() - Single part, minimal random value' );
+	SELECT is( verse.get_random_part( 0.5 , 1 , 0.5 , 0.25 ) , 0.5::DOUBLE PRECISION );
+	SELECT diag_test_name( 'verse.get_random_part() - Two parts, some extra quantity, minimal random value' );
+	SELECT is( verse.get_random_part( 0.75 , 2 , 0.5 , 0.25 ) , 0.25::DOUBLE PRECISION );
+	SELECT diag_test_name( 'verse.get_random_part() - Two parts, maximal quantity, minimal random value' );
+	SELECT is( verse.get_random_part( 1.5 , 2 , 0.5 , 0.25 ) , 0.75::DOUBLE PRECISION );
+
+	/* Second set of tests: results of the function with a fake
+	 * verse.random_deviation() that always returns the maximal possible
+	 * value.
+	 */
+	CREATE OR REPLACE FUNCTION verse.random_deviation( _mean DOUBLE PRECISION , _deviation DOUBLE PRECISION )
+		RETURNS DOUBLE PRECISION
+		STRICT VOLATILE
+	AS $$
+		SELECT $1 + $2;
+	$$ LANGUAGE SQL;
+
+	SELECT diag_test_name( 'verse.get_random_part() - Single part, maximal random value' );
+	SELECT is( verse.get_random_part( 0.5 , 1 , 0.5 , 0.25 ) , 0.5::DOUBLE PRECISION );
+	SELECT diag_test_name( 'verse.get_random_part() - Two parts, some extra quantity, maximal random value' );
+	SELECT is( verse.get_random_part( 1 , 2 , 0.5 , 0.25 ) , 0.75::DOUBLE PRECISION );
+	SELECT diag_test_name( 'verse.get_random_part() - Two parts, minimal quantity, maximal random value' );
+	SELECT is( verse.get_random_part( 0.5 , 2 , 0.5 , 0.25 ) , 0.25::DOUBLE PRECISION );
+
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/053-generator-basics/010-list-random-planets-in.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/053-generator-basics/010-list-random-planets-in.sql
new file mode 100644
index 0000000..ba89819
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/admin/functions/053-generator-basics/010-list-random-planets-in.sql
@@ -0,0 +1,41 @@
+/*
+ * Test the verse.list_random_planets_in() function
+ */
+BEGIN;
+	
+	/* We need two systems with one planet each. Systems will be located at
+	 * (0,0) and (1,1).
+	 */
+	\i utils/strings.sql
+	\i utils/accounts.sql
+	\i utils/naming.sql
+	\i utils/universe.sql
+	SELECT _create_raw_planets( 10 , 'test' );
+
+	/***** TESTS BEGIN HERE *****/ 
+	SELECT plan( 5 );
+
+	SELECT diag_test_name( 'verse.list_random_planets_in() - Empty set if count = 0' );
+	SELECT is_empty( $$
+		SELECT * FROM verse.list_random_planets_in( ROW( 0 , 0 , 1 , 1 ) , 0 )
+	$$ );
+
+	SELECT diag_test_name( 'verse.list_random_planets_in() - Empty set if outside of existing universe' );
+	SELECT is_empty( $$
+		SELECT * FROM verse.list_random_planets_in( ROW( 2 , 2 , 3 , 3 ) , 2 )
+	$$ );
+
+	SELECT diag_test_name( 'verse.list_random_planets_in() - Requested count > actual count' );
+	SELECT is( COUNT(*) , 5::BIGINT )
+		FROM verse.list_random_planets_in( ROW( 1 , 1 , 2 , 2 ) , 7 );
+
+	SELECT diag_test_name( 'verse.list_random_planets_in() - Requested count = actual count' );
+	SELECT is( COUNT(*) , 5::BIGINT )
+		FROM verse.list_random_planets_in( ROW( 1 , 1 , 2 , 2 ) , 5 );
+
+	SELECT diag_test_name( 'verse.list_random_planets_in() - Requested count < actual count' );
+	SELECT is( COUNT(*) , 4::BIGINT )
+		FROM verse.list_random_planets_in( ROW( 1 , 1 , 2 , 2 ) , 4 );
+
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/010-collect-resprov-statistics.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/010-collect-resprov-statistics.sql
new file mode 100644
index 0000000..2b61f59
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/010-collect-resprov-statistics.sql
@@ -0,0 +1,72 @@
+/*
+ * Test the verse.collect_resprov_statistics( ) function
+ */
+BEGIN;
+	\i utils/strings.sql
+	\i utils/resources.sql
+	\i utils/accounts.sql
+	\i utils/naming.sql
+	\i utils/universe.sql
+	SELECT _create_natural_resources( 2 , 'testResource' );
+	SELECT _create_raw_planets( 5 , 'testPlanet' );
+
+	/****** TESTS BEGIN HERE ******/
+	SELECT plan( 6 );
+
+	SELECT verse.collect_resprov_statistics( );
+	
+	SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - Temporary table creation' );
+	SELECT has_table( 'rp_stats' );
+
+	SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - Temporary table rows' );
+	SELECT is( COUNT(*)::INT , 2 ) FROM rp_stats;
+
+	SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - rp_stats contains definition data' );
+	SELECT is( COUNT(*)::INT , 2 )
+		FROM (
+			SELECT ( d.natres_quantity_avg = s.quantity_avg
+					AND d.natres_quantity_dev = s.quantity_dev
+					AND d.natres_difficulty_avg = s.difficulty_avg
+					AND d.natres_difficulty_dev = s.difficulty_dev
+					AND d.natres_recovery_avg = s.recovery_avg
+					AND d.natres_recovery_dev = s.recovery_dev
+					AND d.natres_p_presence = s.presence ) AS ok
+				FROM rp_stats s
+					INNER JOIN defs.natural_resources d
+						USING ( resource_name_id )
+			) sq
+		WHERE sq.ok;
+
+	SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - rp_stats contains planet counts' );
+	SELECT is( COUNT(*)::INT , 2 )
+		FROM (
+			SELECT planets = 5 AS ok
+				FROM rp_stats
+			) sq
+		WHERE sq.ok;
+
+	SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - rp_stats sums at 0 with no providers' );
+	SELECT is( COUNT(*)::INT , 2 )
+		FROM (
+			SELECT ( providers = 0 AND quantity = 0
+					AND difficulty = 0 AND recovery = 0 ) AS ok
+				FROM rp_stats
+			) sq
+		WHERE sq.ok;
+
+	DROP TABLE rp_stats;
+	SELECT _create_resource_provider( 'testPlanet1' , 'testResource1' );
+	SELECT verse.collect_resprov_statistics( );
+
+	SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - rp_stats sums' );
+	SELECT is( COUNT(*)::INT , 1 )
+		FROM (
+			SELECT ( resource_name_id = _get_string( 'testResource1' )
+					AND providers = 1 AND quantity = 100
+					AND difficulty = 0.5 AND recovery = 0.5 ) AS ok
+				FROM rp_stats
+			) sq
+		WHERE sq.ok;
+
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/020-compute-rpp-delta.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/020-compute-rpp-delta.sql
new file mode 100644
index 0000000..2a14193
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/020-compute-rpp-delta.sql
@@ -0,0 +1,56 @@
+/*
+ * Test the verse.compute_rpp_delta( ) function
+ */
+BEGIN;
+	SELECT plan( 12 );
+	
+	/* First set of tests: results of the function with a fake
+	 * verse.random_deviation() that always returns the minimal possible
+	 * value.
+	 */
+	CREATE OR REPLACE FUNCTION verse.random_deviation( _mean DOUBLE PRECISION , _deviation DOUBLE PRECISION )
+		RETURNS DOUBLE PRECISION
+		STRICT VOLATILE
+	AS $$
+		SELECT $1 - $2;
+	$$ LANGUAGE SQL;
+
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - No existing value, random at minimal value' );
+	SELECT is( verse.compute_rpp_delta( 0 , 10 , 0 , 1 , 0.5 )::NUMERIC , 5.0 );
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value below minimum, random at minimal value' );
+	SELECT is( verse.compute_rpp_delta( 10 , 10 , 0 , 1 , 0.5 )::NUMERIC , 10.0 );
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at minimum, random at minimal value' );
+	SELECT is( verse.compute_rpp_delta( 10 , 10 , 5 , 1 , 0.5 )::NUMERIC , 5.0 );
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at average, random at minimal value' );
+	SELECT is( verse.compute_rpp_delta( 10 , 10 , 10 , 1 , 0.5 )::NUMERIC , 5.0 );
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at maximum, random at minimal value' );
+	SELECT is( verse.compute_rpp_delta( 10 , 10 , 15 , 1 , 0.5 )::NUMERIC , 5.0 );
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value over maximum, random at minimal value' );
+	SELECT is( verse.compute_rpp_delta( 10 , 10 , 20 , 1 , 0.5 )::NUMERIC , 5.0 );
+
+	/* Second set of tests: results of the function with a fake
+	 * verse.random_deviation() that always returns the maximal possible
+	 * value.
+	 */
+	CREATE OR REPLACE FUNCTION verse.random_deviation( _mean DOUBLE PRECISION , _deviation DOUBLE PRECISION )
+		RETURNS DOUBLE PRECISION
+		STRICT VOLATILE
+	AS $$
+		SELECT $1 + $2;
+	$$ LANGUAGE SQL;
+
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - No existing value, random at maximal value' );
+	SELECT is( verse.compute_rpp_delta( 0 , 10 , 0 , 1 , 0.5 )::NUMERIC , 15.0 );
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value below minimum, random at maximal value' );
+	SELECT is( verse.compute_rpp_delta( 10 , 10 , 0 , 1 , 0.5 )::NUMERIC , 15.0 );
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at minimum, random at maximal value' );
+	SELECT is( verse.compute_rpp_delta( 10 , 10 , 5 , 1 , 0.5 )::NUMERIC , 15.0 );
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at average, random at maximal value' );
+	SELECT is( verse.compute_rpp_delta( 10 , 10 , 10 , 1 , 0.5 )::NUMERIC , 15.0 );
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value at maximum, random at maximal value' );
+	SELECT is( verse.compute_rpp_delta( 10 , 10 , 15 , 1 , 0.5 )::NUMERIC , 15.0 );
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Existing value over maximum, random at maximal value' );
+	SELECT is( verse.compute_rpp_delta( 10 , 10 , 20 , 1 , 0.5 )::NUMERIC , 10.0 );
+
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/030-create-resource-provider.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/030-create-resource-provider.sql
new file mode 100644
index 0000000..68f7eeb
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/030-create-resource-provider.sql
@@ -0,0 +1,62 @@
+/*
+ * Test the verse.create_resource_provider( ) function
+ */
+BEGIN;
+
+	/* Before any actual testing, we need to drop FK constraints on the RP
+	 * table and create a table which will contain the results of the
+	 * function.
+	 */
+
+	ALTER TABLE verse.resource_providers
+		DROP CONSTRAINT fk_resprov_planet ,
+		DROP CONSTRAINT fk_resprov_resource;
+
+	CREATE TEMPORARY TABLE test_result(
+			_providers_left	INT ,
+			_tot_quantity	DOUBLE PRECISION ,
+			_tot_difficulty	DOUBLE PRECISION ,
+			_tot_recovery	DOUBLE PRECISION
+	) ON COMMIT DROP;
+
+	/* Now we call the function using a crafted resource statistics row which
+	 * will make it easy to check the results.
+	 */
+	INSERT INTO test_result
+		SELECT * FROM verse.create_resource_provider( 12 ,
+			ROW( 34 , 0.0 , 0.0 , 0.0 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 ) ,
+			2 , 10 , 1.5 , 1 );
+
+
+	/***** TESTS BEGIN HERE *****/
+	SELECT plan( 11 );
+
+
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - Output exists' );
+	SELECT is( COUNT(*)::INT , 1 ) FROM test_result;
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - _providers_left updated' );
+	SELECT is( _providers_left , 1 ) FROM test_result;
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - _tot_quantity updated' );
+	SELECT is( _tot_quantity::NUMERIC , 5.0 ) FROM test_result;
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - _tot_difficulty updated' );
+	SELECT is( _tot_difficulty::NUMERIC , 0.75 ) FROM test_result;
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - _tot_recovery updated' );
+	SELECT is( _tot_recovery::NUMERIC , 0.5 ) FROM test_result;
+
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider exists' );
+	SELECT is( COUNT(*)::INT , 1 ) FROM verse.resource_providers;
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider primary key' );
+	SELECT is( COUNT(*)::INT , 1 ) FROM verse.resource_providers
+		WHERE planet_id = 12 AND resource_name_id = 34;
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider is full' );
+	SELECT is( COUNT(*)::INT , 1 ) FROM verse.resource_providers
+		WHERE resprov_quantity = resprov_quantity_max;
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider maximal quantity' );
+	SELECT is( resprov_quantity_max::NUMERIC , 5.0 ) FROM verse.resource_providers;
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider extraction difficulty' );
+	SELECT is( resprov_difficulty::NUMERIC , 0.75 ) FROM verse.resource_providers;
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - Resource provider recovery rate' );
+	SELECT is( resprov_recovery::NUMERIC , 0.5 ) FROM verse.resource_providers;
+
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/040-create-resource-providers-with-type.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/040-create-resource-providers-with-type.sql
new file mode 100644
index 0000000..d491740
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/040-create-resource-providers-with-type.sql
@@ -0,0 +1,129 @@
+/*
+ * Check the variant of verse.create_resource_providers( ) which takes a
+ * resource statistics record as a parameter.
+ */
+BEGIN;
+
+	/* Temporary table which will store information about calls to the
+	 * verse.create_resource_provider( ) function
+	 */
+	CREATE TEMPORARY TABLE test_result(
+		planet		INT ,
+		resource	INT ,
+		quantity	DOUBLE PRECISION ,
+		difficulty	DOUBLE PRECISION ,
+		recovery	DOUBLE PRECISION
+	) ON COMMIT DROP;
+
+	/* Replace verse.list_random_planets_in() with a function that simply
+	 * counts.
+	 */
+	CREATE OR REPLACE FUNCTION verse.list_random_planets_in(
+				_area verse.generator_area_type ,
+				_count INT )
+			RETURNS SETOF INT
+			STRICT VOLATILE
+			SECURITY INVOKER
+		AS $list_random_planets_in$
+	DECLARE
+		i INT;
+	BEGIN
+		FOR i IN 1 .. _count
+		LOOP
+			RETURN NEXT i;
+		END LOOP;
+	END;
+	$list_random_planets_in$ LANGUAGE PLPGSQL;
+	
+	/* Replace verse.create_resource_provider( ) with a function that writes
+	 * to the test_result temporary table.
+	 */
+	CREATE OR REPLACE FUNCTION verse.create_resource_provider(
+			_planet					INT ,
+			_data					verse.resprov_generator_type ,
+			INOUT _providers_left	INT ,
+			INOUT _tot_quantity		DOUBLE PRECISION ,
+			INOUT _tot_difficulty	DOUBLE PRECISION ,
+			INOUT _tot_recovery		DOUBLE PRECISION )
+		STRICT VOLATILE
+		SECURITY INVOKER
+	AS $create_resource_provider$
+	BEGIN
+		INSERT INTO test_result VALUES(
+			_planet , _data.resource_name_id ,
+			_tot_quantity / _providers_left::DOUBLE PRECISION ,
+			_tot_difficulty / _providers_left::DOUBLE PRECISION ,
+			_tot_recovery / _providers_left::DOUBLE PRECISION
+		);
+		
+		_tot_quantity := ( _providers_left - 1 ) * _tot_quantity / _providers_left;
+		_tot_difficulty := ( _providers_left - 1 ) * _tot_difficulty / _providers_left;
+		_tot_recovery := ( _providers_left - 1 ) * _tot_recovery / _providers_left;
+		_providers_left := _providers_left - 1;
+	END;
+	$create_resource_provider$ LANGUAGE PLPGSQL;
+
+	/***** TESTS BEGIN HERE *****/
+	SELECT plan( 11 );
+
+
+	/* First set of test: empty universe */
+	SELECT verse.create_resource_providers(
+		ROW( 0 , 0 , 1 , 1 ) ,
+		ROW( 42 , 0.0 , 0.0 , 0.5 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 )
+	);
+
+	SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Providers created' );
+	SELECT is( COUNT(*)::INT , 10 ) FROM test_result WHERE resource = 42;
+	SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Planet identifiers' );
+	SELECT results_eq(
+		'SELECT planet FROM test_result ORDER BY planet' ,
+		ARRAY[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] 
+	);	
+
+	SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Total quantity' );
+	SELECT is( SUM(quantity)::NUMERIC , 50.0 ) FROM test_result;
+	SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Quantity per planet' );
+	SELECT is( COUNT(*)::INT , 10 ) FROM test_result
+		WHERE quantity = 5;
+
+	SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Total difficulty' );
+	SELECT is( SUM(difficulty)::NUMERIC , 7.5 ) FROM test_result;
+	SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Difficulty per planet' );
+	SELECT is( COUNT(*)::INT , 10 ) FROM test_result
+		WHERE difficulty = 0.75;
+
+	SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Total recovery rate' );
+	SELECT is( SUM(recovery)::NUMERIC , 5.0 ) FROM test_result;
+	SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Empty universe - Recovery rate per planet' );
+	SELECT is( COUNT(*)::INT , 10 ) FROM test_result
+		WHERE recovery = 0.5;
+
+
+	/* Second set of tests: balancing providers presence */
+	SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Providers created in balanced universe' );
+	DELETE FROM test_result;
+	SELECT verse.create_resource_providers(
+		ROW( 0 , 0 , 1 , 1 ) ,
+		ROW( 42 , 20.0 , 10.0 , 0.5 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 )
+	);
+	SELECT is( COUNT(*)::INT , 10 ) FROM test_result WHERE resource = 42;
+
+	SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Providers created in universe with low providers count' );
+	DELETE FROM test_result;
+	SELECT verse.create_resource_providers(
+		ROW( 0 , 0 , 1 , 1 ) ,
+		ROW( 42 , 20.0 , 5.0 , 0.5 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 )
+	);
+	SELECT is( COUNT(*)::INT , 15 ) FROM test_result WHERE resource = 42;
+
+	SELECT diag_test_name( 'verse.create_resource_provider( area , stats ) - Providers created in universe with high providers count' );
+	DELETE FROM test_result;
+	SELECT verse.create_resource_providers(
+		ROW( 0 , 0 , 1 , 1 ) ,
+		ROW( 42 , 20.0 , 15.0 , 0.5 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 )
+	);
+	SELECT is( COUNT(*)::INT , 5 ) FROM test_result WHERE resource = 42;
+
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/050-create-resource-providers.sql b/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/050-create-resource-providers.sql
new file mode 100644
index 0000000..6c5ea83
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/admin/functions/055-generator-resources/050-create-resource-providers.sql
@@ -0,0 +1,35 @@
+/*
+ * Test the "main" verse.create_resource_providers( ) function
+ */
+BEGIN;
+
+	/* Create a dummy rp_stats table that contains a few identifiers */
+	 CREATE TEMP TABLE rp_stats
+	 	OF verse.resprov_generator_type
+	 	ON COMMIT DROP;
+	 INSERT INTO rp_stats ( resource_name_id )
+	 	VALUES ( 1 ) , ( 2 ) , ( 3 ) , ( 4 );
+
+	 /* Replace the verse.create_resource_providers( area , type ) function
+	  * with a function that deletes the resource type it was given from
+	  * rp_stats.
+	  */
+	CREATE OR REPLACE FUNCTION verse.create_resource_providers(
+			_area verse.generator_area_type ,
+			_data verse.resprov_generator_type )
+		RETURNS VOID
+		STRICT VOLATILE
+		SECURITY INVOKER
+	AS $create_resource_providers$
+	BEGIN
+		DELETE FROM rp_stats WHERE resource_name_id = _data.resource_name_id;
+	END;
+	$create_resource_providers$ LANGUAGE PLPGSQL;
+
+	SELECT plan(1);
+	SELECT diag_test_name( 'verse.create_resource_providers( area ) - Calls to per-type function' );
+	SELECT verse.create_resource_providers( ROW( 0 , 0 , 1 , 1 ) );
+	SELECT is( COUNT(*)::INT , 0 ) FROM rp_stats;
+	
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
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/145-resource-providers/010-compute-provider-regeneration.sql
similarity index 100%
rename from legacyworlds-server-data/db-structure/tests/admin/functions/verse/14500-compute-provider-regeneration.sql
rename to legacyworlds-server-data/db-structure/tests/admin/functions/145-resource-providers/010-compute-provider-regeneration.sql
diff --git a/legacyworlds-server-data/db-structure/tests/user/priv/functions/050-computation/010-get-random-part.sql b/legacyworlds-server-data/db-structure/tests/user/priv/functions/050-computation/010-get-random-part.sql
new file mode 100644
index 0000000..5ce2907
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/user/priv/functions/050-computation/010-get-random-part.sql
@@ -0,0 +1,14 @@
+/*
+ * Test privileges on verse.get_random_part()
+ */
+BEGIN;
+
+	SELECT plan( 1 );
+	
+	SELECT diag_test_name( 'verse.get_random_part( ) - Privileges' );
+	PREPARE _test_this AS
+		SELECT verse.get_random_part( 0.5 , 1 , 0.5 , 0.25 );
+	SELECT throws_ok( '_test_this' , 42501 );
+	
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/user/priv/functions/053-generator-basics/010-list-random-planets-in.sql b/legacyworlds-server-data/db-structure/tests/user/priv/functions/053-generator-basics/010-list-random-planets-in.sql
new file mode 100644
index 0000000..0f124ca
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/user/priv/functions/053-generator-basics/010-list-random-planets-in.sql
@@ -0,0 +1,14 @@
+/*
+ * Test privileges on verse.list_random_planets_in()
+ */
+BEGIN;
+
+	SELECT plan( 1 );
+	
+	SELECT diag_test_name( 'verse.list_random_planets_in( ) - Privileges' );
+	PREPARE _test_this AS
+		SELECT * FROM verse.list_random_planets_in( ROW( 2 , 2 , 3 , 3 ) , 2 );
+	SELECT throws_ok( '_test_this' , 42501 );
+	
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/010-collect-resprov-statistics.sql b/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/010-collect-resprov-statistics.sql
new file mode 100644
index 0000000..a47c717
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/010-collect-resprov-statistics.sql
@@ -0,0 +1,14 @@
+/*
+ * Test privileges on verse.collect_resprov_statistics()
+ */
+BEGIN;
+
+	SELECT plan( 1 );
+	
+	SELECT diag_test_name( 'verse.collect_resprov_statistics( ) - Privileges' );
+	PREPARE _test_this AS
+		SELECT verse.collect_resprov_statistics( );
+	SELECT throws_ok( '_test_this' , 42501 );
+	
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/020-compute-rpp-delta.sql b/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/020-compute-rpp-delta.sql
new file mode 100644
index 0000000..f309075
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/020-compute-rpp-delta.sql
@@ -0,0 +1,14 @@
+/*
+ * Test privileges on verse.compute_rpp_delta( )
+ */
+BEGIN;
+
+	SELECT plan( 1 );
+	
+	SELECT diag_test_name( 'verse.compute_rpp_delta( ) - Privileges' );
+	PREPARE _test_this AS
+		SELECT verse.compute_rpp_delta( 10 , 10 , 20 , 1 , 0.5 );
+	SELECT throws_ok( '_test_this' , 42501 );
+	
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/030-create-resource-provider.sql b/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/030-create-resource-provider.sql
new file mode 100644
index 0000000..0c1368f
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/030-create-resource-provider.sql
@@ -0,0 +1,16 @@
+/*
+ * Test privileges on verse.create_resource_provider( )
+ */
+BEGIN;
+
+	SELECT plan( 1 );
+	
+	SELECT diag_test_name( 'verse.create_resource_provider( ) - Privileges' );
+	PREPARE _test_this AS
+		SELECT * FROM verse.create_resource_provider( 12 ,
+			ROW( 34 , 0.0 , 0.0 , 0.0 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 ) ,
+			2 , 10 , 1.5 , 1 );
+	SELECT throws_ok( '_test_this' , 42501 );
+	
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/040-create-resource-providers-with-type.sql b/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/040-create-resource-providers-with-type.sql
new file mode 100644
index 0000000..5081dd8
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/040-create-resource-providers-with-type.sql
@@ -0,0 +1,18 @@
+/*
+ * Test privileges on the variant of verse.create_resource_providers( ) which
+ * takes a resource statistics record as a parameter.
+ */
+BEGIN;
+
+	SELECT plan( 1 );
+	
+	SELECT diag_test_name( 'verse.create_resource_providers( area , type ) - Privileges' );
+	PREPARE _test_this AS
+		SELECT verse.create_resource_providers(
+			ROW( 0 , 0 , 1 , 1 ) ,
+			ROW( 42 , 0.0 , 0.0 , 0.5 , 0.0 , 5.0 , 0.0 , 0.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0 )
+		);
+	SELECT throws_ok( '_test_this' , 42501 );
+	
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file
diff --git a/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/050-create-resource-providers.sql b/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/050-create-resource-providers.sql
new file mode 100644
index 0000000..a1b1cf7
--- /dev/null
+++ b/legacyworlds-server-data/db-structure/tests/user/priv/functions/055-generator-resources.sql/050-create-resource-providers.sql
@@ -0,0 +1,14 @@
+/*
+ * Test privileges on the "main" verse.create_resource_providers( ) function
+ */
+BEGIN;
+
+	SELECT plan( 1 );
+	
+	SELECT diag_test_name( 'verse.create_resource_providers( area ) - Privileges' );
+	PREPARE _test_this AS
+		SELECT verse.create_resource_providers( ROW( 0 , 0 , 1 , 1 ) );
+	SELECT throws_ok( '_test_this' , 42501 );
+	
+	SELECT * FROM finish( );
+ROLLBACK;
\ No newline at end of file