-- LegacyWorlds Beta 6
-- PostgreSQL database scripts
--
-- Game updates - control functions
--
-- Copyright(C) 2004-2010, DeepClone Development
-- --------------------------------------------------------



--
-- Start a tick
--

CREATE OR REPLACE FUNCTION sys.start_tick( OUT tick_id BIGINT )
		STRICT VOLATILE
		SECURITY DEFINER
	AS $$
DECLARE
	n_tick	BIGINT;
	c_tick	BIGINT;
BEGIN
	-- Get next / current tick
	SELECT INTO n_tick , c_tick next_tick , current_tick
		FROM sys.status
		WHERE maintenance_start IS NULL
		FOR UPDATE;
	IF NOT FOUND OR c_tick IS NOT NULL THEN
		tick_id := NULL;
		RETURN;
	END IF;

	-- Prepare game updates
	UPDATE sys.updates SET last_tick = n_tick , status = 'FUTURE'
		WHERE last_tick < n_tick;

	-- Update system status
	UPDATE sys.status SET current_tick = n_tick , next_tick = n_tick + 1;
	
	tick_id := n_tick;
END;
$$ LANGUAGE plpgsql;

GRANT EXECUTE ON FUNCTION sys.start_tick( ) TO :dbuser;



--
-- Marks a tick as completed
--

CREATE OR REPLACE FUNCTION sys.end_tick( IN tick_id BIGINT )
		RETURNS VOID
		STRICT VOLATILE
		SECURITY DEFINER
	AS $$
BEGIN
	UPDATE events.events SET status = 'READY'
		WHERE status = 'TICK' AND tick = tick_id;
	UPDATE sys.status SET current_tick = NULL;
	PERFORM msgs.deliver_internal( );
END;
$$ LANGUAGE plpgsql;

GRANT EXECUTE ON FUNCTION sys.end_tick( BIGINT ) TO :dbuser;



--
-- Check if a tick got "stuck"
--

CREATE OR REPLACE FUNCTION sys.check_stuck_tick( OUT tick_id BIGINT )
		STRICT VOLATILE
		SECURITY DEFINER
	AS $$
DECLARE
	c_tick	BIGINT;
	u_count	INT;
BEGIN
	-- Get next / current tick
	SELECT INTO c_tick current_tick
		FROM sys.status
		WHERE maintenance_start IS NULL
		FOR UPDATE;
	IF NOT FOUND OR c_tick IS NULL THEN
		tick_id := NULL;
		RETURN;
	END IF;
	
	-- Are there any updates left?
	SELECT INTO u_count count(*) FROM sys.updates
		WHERE status = 'FUTURE' AND last_tick = c_tick;
	IF u_count = 0 THEN
		PERFORM sys.end_tick( c_tick );
		tick_id := NULL;
	ELSE
		tick_id := c_tick;
	END IF;
END;
$$ LANGUAGE plpgsql;

GRANT EXECUTE ON FUNCTION sys.check_stuck_tick( ) TO :dbuser;



--
-- Prepare game updates
--
-- Parameters:
--  u_id		Current update identifier
--	u_type		Type of game updates to prepare
--
-- Returns:
--	has_more	TRUE if there are more updates, FALSE otherwise
--

CREATE OR REPLACE FUNCTION sys.prepare_updates( IN u_id BIGINT , IN u_type update_type , OUT has_more BOOLEAN )
		STRICT VOLATILE
		SECURITY DEFINER
	AS $$
BEGIN
	UPDATE sys.updates SET status = 'PROCESSING'
		WHERE id IN (
			SELECT id FROM sys.updates
				WHERE gu_type = u_type
					AND last_tick = u_id
					AND status = 'FUTURE'
				ORDER BY id
				LIMIT sys.get_constant( 'game.batchSize' )::BIGINT
			);

	has_more := FOUND;
END;
$$ LANGUAGE plpgsql;

GRANT EXECUTE ON FUNCTION sys.prepare_updates( BIGINT , update_type ) TO :dbuser;



--
-- Execute procedural game updates
--
-- Parameters:
--	c_tick	Current tick identifier
--	u_type	Type of updates to execute
--

CREATE OR REPLACE FUNCTION sys.exec_update_proc( IN c_tick BIGINT , IN u_type update_type )
		RETURNS VOID
		STRICT VOLATILE
		SECURITY DEFINER
	AS $$
BEGIN
	EXECUTE 'SELECT sys.process_' || lower( u_type::TEXT ) || '_updates( $1 )'
			USING c_tick;
END; 
$$ LANGUAGE plpgsql;

GRANT EXECUTE ON FUNCTION sys.exec_update_proc( BIGINT , update_type ) TO :dbuser;


--
-- Mark updates as processed
--
-- Parameters:
--	c_tick	Current tick identifier
--	u_type	Type of updates to execute
--

CREATE OR REPLACE FUNCTION sys.updates_processed( IN c_tick BIGINT , IN u_type update_type )
	RETURNS VOID
	STRICT VOLATILE
	SECURITY DEFINER
AS $$
		UPDATE sys.updates SET status = 'PROCESSED'
			WHERE status = 'PROCESSING'
				AND last_tick = $1
				AND gu_type = $2
$$ LANGUAGE SQL;

GRANT EXECUTE ON FUNCTION sys.updates_processed( BIGINT , update_type ) TO :dbuser;
