Initial commit: Shattered Void MMO foundation

- Complete PostgreSQL database schema with 21+ tables
- Express.js server with dual authentication (player/admin)
- WebSocket support for real-time features
- Comprehensive middleware (auth, validation, logging, security)
- Game systems: colonies, resources, fleets, research, factions
- Plugin-based combat architecture
- Admin panel foundation
- Production-ready logging and error handling
- Docker support and CI/CD ready
- Complete project structure following CLAUDE.md patterns

🤖 Generated with Claude Code (https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
MegaProxy 2025-08-02 02:13:05 +00:00
commit 1a60cf55a3
69 changed files with 24471 additions and 0 deletions

View file

@ -0,0 +1,192 @@
exports.up = async function(knex) {
// System configuration with hot-reloading support
await knex.schema.createTable('system_config', (table) => {
table.increments('id').primary();
table.string('config_key', 100).unique().notNullable();
table.jsonb('config_value').notNullable();
table.string('config_type', 20).notNullable().checkIn(['string', 'number', 'boolean', 'json', 'array']);
table.text('description');
table.boolean('requires_restart').defaultTo(false);
table.boolean('is_public').defaultTo(false); // Can be exposed to client
table.timestamp('created_at').defaultTo(knex.fn.now());
table.timestamp('updated_at').defaultTo(knex.fn.now());
table.integer('updated_by'); // Will reference admin_users(id) after table creation
});
// Game tick system with user grouping
await knex.schema.createTable('game_tick_config', (table) => {
table.increments('id').primary();
table.integer('tick_interval_ms').notNullable().defaultTo(60000);
table.integer('user_groups_count').notNullable().defaultTo(10);
table.integer('max_retry_attempts').notNullable().defaultTo(5);
table.integer('bonus_tick_threshold').notNullable().defaultTo(3);
table.boolean('is_active').defaultTo(true);
table.timestamp('created_at').defaultTo(knex.fn.now());
table.timestamp('updated_at').defaultTo(knex.fn.now());
});
await knex.schema.createTable('game_tick_log', (table) => {
table.bigIncrements('id').primary();
table.bigInteger('tick_number').notNullable();
table.integer('user_group').notNullable();
table.timestamp('started_at').notNullable();
table.timestamp('completed_at');
table.string('status', 20).notNullable().checkIn(['running', 'completed', 'failed', 'retrying']);
table.integer('retry_count').defaultTo(0);
table.text('error_message');
table.integer('processed_players').defaultTo(0);
table.jsonb('performance_metrics');
table.timestamp('created_at').defaultTo(knex.fn.now());
table.index(['tick_number']);
table.index(['user_group']);
table.index(['status']);
});
// Event system configuration
await knex.schema.createTable('event_types', (table) => {
table.increments('id').primary();
table.string('name', 100).unique().notNullable();
table.text('description');
table.string('trigger_type', 20).notNullable().checkIn(['admin', 'player', 'system', 'mixed']);
table.boolean('is_active').defaultTo(true);
table.jsonb('config_schema'); // JSON schema for event configuration
table.timestamp('created_at').defaultTo(knex.fn.now());
});
await knex.schema.createTable('event_instances', (table) => {
table.bigIncrements('id').primary();
table.integer('event_type_id').notNullable().references('id').inTable('event_types');
table.string('name', 200).notNullable();
table.text('description');
table.jsonb('config').notNullable();
table.timestamp('start_time');
table.timestamp('end_time');
table.string('status', 20).notNullable().checkIn(['scheduled', 'active', 'completed', 'cancelled']);
table.integer('created_by'); // Will reference admin_users(id) after table creation
table.timestamp('created_at').defaultTo(knex.fn.now());
table.timestamp('updated_at').defaultTo(knex.fn.now());
table.index(['event_type_id']);
table.index(['status']);
table.index(['start_time']);
});
// Plugin system for extensibility
await knex.schema.createTable('plugins', (table) => {
table.increments('id').primary();
table.string('name', 100).unique().notNullable();
table.string('version', 20).notNullable();
table.text('description');
table.string('plugin_type', 50).notNullable(); // 'combat', 'event', 'resource', etc.
table.boolean('is_active').defaultTo(false);
table.jsonb('config');
table.jsonb('dependencies'); // Array of required plugins
table.jsonb('hooks'); // Available hook points
table.timestamp('created_at').defaultTo(knex.fn.now());
table.timestamp('updated_at').defaultTo(knex.fn.now());
});
// Insert initial system configuration
await knex('system_config').insert([
{
config_key: 'game_tick_interval_ms',
config_value: JSON.stringify(60000),
config_type: 'number',
description: 'Game tick interval in milliseconds',
is_public: false,
},
{
config_key: 'max_user_groups',
config_value: JSON.stringify(10),
config_type: 'number',
description: 'Maximum number of user groups for tick processing',
is_public: false,
},
{
config_key: 'max_retry_attempts',
config_value: JSON.stringify(5),
config_type: 'number',
description: 'Maximum retry attempts for failed ticks',
is_public: false,
},
{
config_key: 'data_retention_days',
config_value: JSON.stringify(30),
config_type: 'number',
description: 'Default data retention period in days',
is_public: false,
},
{
config_key: 'max_colonies_per_player',
config_value: JSON.stringify(10),
config_type: 'number',
description: 'Maximum colonies a player can own',
is_public: true,
},
{
config_key: 'starting_resources',
config_value: JSON.stringify({ scrap: 1000, energy: 500, data_cores: 0, rare_elements: 0 }),
config_type: 'json',
description: 'Starting resources for new players',
is_public: false,
},
{
config_key: 'websocket_ping_interval',
config_value: JSON.stringify(30000),
config_type: 'number',
description: 'WebSocket ping interval in milliseconds',
is_public: false,
},
]);
// Insert initial game tick configuration
await knex('game_tick_config').insert({
tick_interval_ms: 60000,
user_groups_count: 10,
max_retry_attempts: 5,
bonus_tick_threshold: 3,
});
// Insert initial event types
await knex('event_types').insert([
{
name: 'galaxy_crisis',
description: 'Major galaxy-wide crisis events',
trigger_type: 'admin',
config_schema: JSON.stringify({ duration_hours: { type: 'number', min: 1, max: 168 } }),
},
{
name: 'discovery_event',
description: 'Random discovery events triggered by exploration',
trigger_type: 'player',
config_schema: JSON.stringify({ discovery_type: { type: 'string', enum: ['artifact', 'technology', 'resource'] } }),
},
{
name: 'faction_war',
description: 'Large-scale conflicts between factions',
trigger_type: 'mixed',
config_schema: JSON.stringify({ participating_factions: { type: 'array', items: { type: 'number' } } }),
},
]);
// Insert initial plugin for basic combat
await knex('plugins').insert({
name: 'basic_combat',
version: '1.0.0',
description: 'Basic instant combat resolution system',
plugin_type: 'combat',
is_active: true,
config: JSON.stringify({ damage_variance: 0.1, experience_gain: 1.0 }),
hooks: JSON.stringify(['pre_combat', 'post_combat', 'damage_calculation']),
});
};
exports.down = async function(knex) {
await knex.schema.dropTableIfExists('plugins');
await knex.schema.dropTableIfExists('event_instances');
await knex.schema.dropTableIfExists('event_types');
await knex.schema.dropTableIfExists('game_tick_log');
await knex.schema.dropTableIfExists('game_tick_config');
await knex.schema.dropTableIfExists('system_config');
};