OrbitMC commited on
Commit
1e4f039
·
verified ·
1 Parent(s): cc67353

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +126 -75
server.js CHANGED
@@ -6,24 +6,51 @@ const app = express();
6
  const server = http.createServer(app);
7
  const io = new Server(server, {
8
  cors: { origin: "*" },
9
- pingTimeout: 60000, // No timeout unless connection lost
10
  });
11
 
12
  app.use(express.static('public'));
13
 
14
- // CONFIG
15
- const GROWTH_RATE = 3; // Pixels per tick (must match client)
16
- const PLATFORM_HEIGHT = 100;
17
  const PLAYER_SIZE = 20;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- // STATE
20
- let platforms = []; // Shared world for everyone
21
- let players = {}; // { socketId: { id, name, score, x, state, ... } }
22
 
23
- // Generate infinite world logic
24
  function generatePlatform(prevX, prevW) {
25
- const gap = 80 + Math.random() * 120; // Random gap
26
- const width = 40 + Math.random() * 60; // Random width
27
  return {
28
  x: prevX + prevW + gap,
29
  w: width,
@@ -31,7 +58,7 @@ function generatePlatform(prevX, prevW) {
31
  };
32
  }
33
 
34
- // Init first few platforms
35
  platforms.push({ x: 50, w: 100, id: 'start' });
36
  for (let i = 0; i < 10; i++) {
37
  const last = platforms[platforms.length - 1];
@@ -39,111 +66,135 @@ for (let i = 0; i < 10; i++) {
39
  }
40
 
41
  io.on('connection', (socket) => {
42
- console.log(`Player connected: ${socket.id}`);
43
-
44
- // Init Player State
45
- players[socket.id] = {
46
- id: socket.id,
47
- name: "Guest",
48
- score: 0,
49
- x: platforms[0].x + platforms[0].w - PLAYER_SIZE - 5,
50
- currentPlatIndex: 0,
51
- stickHeight: 0,
52
- isGrowing: false,
53
- growStartTime: 0,
54
- state: 'ready' // ready, growing, moving, dead
55
- };
56
 
57
- // 1. Join Game
58
- socket.on('join', (name) => {
59
- players[socket.id].name = name.substring(0, 12) || "Guest";
60
- // Send initial World State
 
 
 
 
 
 
 
 
61
  socket.emit('init', {
62
  id: socket.id,
63
  platforms: platforms,
64
  players: players
65
  });
66
- io.emit('player_update', players[socket.id]); // Tell others
 
 
67
  });
68
 
69
- // 2. Start Growing Stick
70
  socket.on('start_grow', () => {
 
71
  const p = players[socket.id];
72
- if (p.state !== 'ready') return;
73
 
74
- p.state = 'growing';
75
- p.growStartTime = Date.now();
76
  p.isGrowing = true;
 
77
 
78
- // Broadcast visual state to others
79
- io.emit('player_action', { id: socket.id, type: 'grow_start' });
80
  });
81
 
82
- // 3. Stop Growing (The Critical Physics Check)
83
  socket.on('stop_grow', () => {
 
84
  const p = players[socket.id];
85
- if (p.state !== 'growing') return;
86
 
87
  const duration = Date.now() - p.growStartTime;
88
- // Server calculates length based on time. Client cannot fake stick length.
89
- // We divide by approx frame rate (16ms) to match client visual loop
90
- // Or simpler: stick grows based on ms duration directly
91
- // Let's align with the client loop logic: roughly 60fps loop adds 3px
92
- // 3px per ~16.6ms = ~0.18px per ms
93
- p.stickHeight = Math.min((duration / 16.66) * GROWTH_RATE, 2000);
94
-
95
  p.isGrowing = false;
96
- p.state = 'moving';
97
 
98
- io.emit('player_action', { id: socket.id, type: 'grow_stop', height: p.stickHeight });
 
 
 
 
 
99
 
100
- // Logic Check
101
  const currentPlat = platforms[p.currentPlatIndex];
102
  const nextPlat = platforms[p.currentPlatIndex + 1];
103
 
104
- // Ensure we have platforms
105
  if (!nextPlat) {
106
  const last = platforms[platforms.length - 1];
107
- platforms.push(generatePlatform(last.x, last.w));
108
- io.emit('new_platform', platforms[platforms.length - 1]);
 
109
  }
110
 
111
- const stickEnd = currentPlat.x + currentPlat.w + p.stickHeight;
112
-
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  setTimeout(() => {
114
- // Did they land?
115
- if (stickEnd >= nextPlat.x && stickEnd <= nextPlat.x + nextPlat.w) {
116
- // SUCCESS
117
- p.score++;
118
  p.currentPlatIndex++;
119
- p.x = nextPlat.x + nextPlat.w - PLAYER_SIZE - 5;
120
- p.state = 'ready';
121
 
122
- // Generate more world if needed
123
  if (platforms.length < p.currentPlatIndex + 5) {
124
- const last = platforms[platforms.length - 1];
125
- const newPlat = generatePlatform(last.x, last.w);
126
- platforms.push(newPlat);
127
- io.emit('new_platform', newPlat);
128
  }
129
-
130
- io.emit('player_result', { id: socket.id, success: true, score: p.score, x: p.x });
131
  } else {
132
- // FAIL
133
  p.score = 0;
134
  p.currentPlatIndex = 0;
135
- p.x = platforms[0].x + platforms[0].w - PLAYER_SIZE - 5;
136
- p.state = 'ready';
137
-
138
- io.emit('player_result', { id: socket.id, success: false, score: 0, x: p.x });
139
  }
140
- }, 1000); // Wait for fall animation roughly
 
 
 
 
 
 
 
 
 
 
 
 
141
  });
142
 
143
  socket.on('disconnect', () => {
144
- console.log(`Player disconnected: ${socket.id}`);
145
- delete players[socket.id];
146
- io.emit('player_leave', socket.id);
 
147
  });
148
  });
149
 
 
6
  const server = http.createServer(app);
7
  const io = new Server(server, {
8
  cors: { origin: "*" },
9
+ pingTimeout: 60000,
10
  });
11
 
12
  app.use(express.static('public'));
13
 
14
+ // --- CONFIGURATION ---
 
 
15
  const PLAYER_SIZE = 20;
16
+ // Growth rate (pixels per ms).
17
+ // Must match Client exactly for prediction to work.
18
+ // 3px per frame (at 60fps) ~= 0.18px per ms
19
+ const GROWTH_SPEED = 0.18;
20
+
21
+ // --- ACCESS CODES ---
22
+ // In HuggingFace, add a secret named 'GAME_CODES'
23
+ // For now, we generate them in memory if not found.
24
+ let VALID_CODES = new Set();
25
+ const FIXED_CODE = "676900";
26
+
27
+ function loadCodes() {
28
+ if (process.env.GAME_CODES) {
29
+ // Expecting comma separated codes
30
+ const codes = process.env.GAME_CODES.split(',');
31
+ codes.forEach(c => VALID_CODES.add(c.trim()));
32
+ } else {
33
+ // Generate 29 random codes + fixed code
34
+ console.log("--- GENERATING NEW ACCESS CODES (Save these to Secrets) ---");
35
+ VALID_CODES.add(FIXED_CODE);
36
+ console.log(`1. ${FIXED_CODE} (Master)`);
37
+ for(let i=1; i<30; i++) {
38
+ const code = Math.floor(100000 + Math.random() * 900000).toString();
39
+ VALID_CODES.add(code);
40
+ console.log(`${i+1}. ${code}`);
41
+ }
42
+ console.log("-----------------------------------------------------------");
43
+ }
44
+ }
45
+ loadCodes();
46
 
47
+ // --- GAME STATE ---
48
+ let platforms = [];
49
+ let players = {};
50
 
 
51
  function generatePlatform(prevX, prevW) {
52
+ const gap = 80 + Math.random() * 120;
53
+ const width = 40 + Math.random() * 60;
54
  return {
55
  x: prevX + prevW + gap,
56
  w: width,
 
58
  };
59
  }
60
 
61
+ // Init World
62
  platforms.push({ x: 50, w: 100, id: 'start' });
63
  for (let i = 0; i < 10; i++) {
64
  const last = platforms[platforms.length - 1];
 
66
  }
67
 
68
  io.on('connection', (socket) => {
69
+ let isAuthenticated = false;
70
+
71
+ // 1. Login with Code
72
+ socket.on('login', (data) => {
73
+ const { name, code } = data;
74
+
75
+ if (!VALID_CODES.has(code)) {
76
+ socket.emit('auth_error', 'Invalid Access Code');
77
+ return;
78
+ }
79
+
80
+ isAuthenticated = true;
81
+ const cleanName = (name || "Guest").substring(0, 12);
 
82
 
83
+ // Init Player
84
+ players[socket.id] = {
85
+ id: socket.id,
86
+ name: cleanName,
87
+ score: 0,
88
+ x: platforms[0].x + platforms[0].w - PLAYER_SIZE - 5,
89
+ currentPlatIndex: 0,
90
+ growStartTime: 0,
91
+ isGrowing: false
92
+ };
93
+
94
+ // Send Success & World State
95
  socket.emit('init', {
96
  id: socket.id,
97
  platforms: platforms,
98
  players: players
99
  });
100
+
101
+ // Broadcast to others
102
+ socket.broadcast.emit('player_join', players[socket.id]);
103
  });
104
 
105
+ // 2. Start Growing (Timestamp Authoritative)
106
  socket.on('start_grow', () => {
107
+ if (!isAuthenticated || !players[socket.id]) return;
108
  const p = players[socket.id];
 
109
 
 
 
110
  p.isGrowing = true;
111
+ p.growStartTime = Date.now();
112
 
113
+ // Relay to others for visual interpolation
114
+ socket.broadcast.emit('player_action', { id: socket.id, type: 'grow_start' });
115
  });
116
 
117
+ // 3. Stop Growing (The Physics Check)
118
  socket.on('stop_grow', () => {
119
+ if (!isAuthenticated || !players[socket.id]) return;
120
  const p = players[socket.id];
121
+ if (!p.isGrowing) return;
122
 
123
  const duration = Date.now() - p.growStartTime;
 
 
 
 
 
 
 
124
  p.isGrowing = false;
 
125
 
126
+ // Calculate Authoritative Height
127
+ // Cap max height to prevent exploit
128
+ let stickHeight = Math.min(duration * GROWTH_SPEED, 2000);
129
+
130
+ // Notify others immediately of the result so they see the animation
131
+ socket.broadcast.emit('player_action', { id: socket.id, type: 'grow_stop', height: stickHeight });
132
 
133
+ // --- SERVER PHYSICS CHECK ---
134
  const currentPlat = platforms[p.currentPlatIndex];
135
  const nextPlat = platforms[p.currentPlatIndex + 1];
136
 
137
+ // Generate map if running out
138
  if (!nextPlat) {
139
  const last = platforms[platforms.length - 1];
140
+ const newPlat = generatePlatform(last.x, last.w);
141
+ platforms.push(newPlat);
142
+ io.emit('new_platform', newPlat);
143
  }
144
 
145
+ const stickEnd = currentPlat.x + currentPlat.w + stickHeight;
146
+ const safeStart = nextPlat.x;
147
+ const safeEnd = nextPlat.x + nextPlat.w;
148
+ const perfectCenter = nextPlat.x + (nextPlat.w / 2);
149
+
150
+ let success = false;
151
+ let perfect = false;
152
+
153
+ if (stickEnd >= safeStart && stickEnd <= safeEnd) {
154
+ success = true;
155
+ // Check for perfect center hit (optional bonus logic)
156
+ if (Math.abs(stickEnd - perfectCenter) < 5) perfect = true;
157
+ }
158
+
159
+ // Wait for animation duration (simulated) then update state
160
  setTimeout(() => {
161
+ if (success) {
162
+ p.score += perfect ? 2 : 1;
 
 
163
  p.currentPlatIndex++;
164
+ p.x = nextPlat.x + nextPlat.w - PLAYER_SIZE - 5; // Target X
 
165
 
166
+ // Infinite Gen
167
  if (platforms.length < p.currentPlatIndex + 5) {
168
+ const last = platforms[platforms.length - 1];
169
+ const newPlat = generatePlatform(last.x, last.w);
170
+ platforms.push(newPlat);
171
+ io.emit('new_platform', newPlat);
172
  }
 
 
173
  } else {
 
174
  p.score = 0;
175
  p.currentPlatIndex = 0;
176
+ p.x = platforms[0].x + platforms[0].w - PLAYER_SIZE - 5; // Reset X
 
 
 
177
  }
178
+
179
+ // RECONCILIATION PACKET
180
+ // We tell everyone the result.
181
+ // The local client will compare this. If they predicted correctly, nothing happens visually.
182
+ // If they cheated, this packet will snap them back to reality.
183
+ io.emit('player_result', {
184
+ id: socket.id,
185
+ success: success,
186
+ score: p.score,
187
+ x: p.x
188
+ });
189
+
190
+ }, 1000); // 1s represents the fall/walk animation time
191
  });
192
 
193
  socket.on('disconnect', () => {
194
+ if (players[socket.id]) {
195
+ delete players[socket.id];
196
+ io.emit('player_leave', socket.id);
197
+ }
198
  });
199
  });
200