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:
commit
1a60cf55a3
69 changed files with 24471 additions and 0 deletions
192
src/database/migrations/001_initial_system_tables.js
Normal file
192
src/database/migrations/001_initial_system_tables.js
Normal 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');
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue