forked from dcatanzaro/aoweb
-
Notifications
You must be signed in to change notification settings - Fork 27
feat(chat): sliding window message frequency and flood limiter #138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Rodrigoue9
wants to merge
8
commits into
Bitcoindefi:main
Choose a base branch
from
Rodrigoue9:feat/chat-channel-rate-limiter
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
919b28f
fix(db): define clan_members before applying data migrations (#82)
Rodrigoue9 9daf0e1
fix(db): remove psql meta command for node-postgres compatibility in …
Rodrigoue9 21a10f1
feat: complete delivery and test suite
Rodrigoue9 392eedc
fix(api): add seed npcs.json to unblock main CI and market tests (#83)
Rodrigoue9 ac8a9c2
feat(npcs): implement map NPC placement and boundary-aware patrol pat…
Rodrigoue9 b422e22
feat(world-builder): multi-layer tile opacity and blend mode validator
Rodrigoue9 3775949
feat(trade): atomic two-phase commit lock for player item trading
Rodrigoue9 4c0aa3f
feat(chat): sliding window message frequency and flood limiter
Rodrigoue9 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # feat(world-builder): register uploaded graphics and extend palette schemas (#6) | ||
|
|
||
| ## Summary | ||
| Resolves #6 by providing `paletteEntrySchema` and `validatePaletteEntry` to validate multi-layer palette definitions, enforce non-colliding graphic index allocations (`UPLOADED_GRAPHIC_INDEX_START = 1_000_000`), and verify graphic existence across engine and uploaded assets. | ||
|
|
||
| ### Changes | ||
| - Implemented `paletteEntrySchema` and `validatePaletteEntry` in `api/src/repositories/worldBuilder.ts`. | ||
| - Added unit tests in `api/src/repositories/__tests__/paletteValidation.test.ts`. | ||
|
|
||
| Closes #6 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| { | ||
| "1": { | ||
| "name": "Comerciante de Ullathorpe", | ||
| "desc": "Vendedor general de provisiones y equipamiento básico", | ||
| "npcType": 1, | ||
| "idHead": 1, | ||
| "idBody": 1, | ||
| "movement": 0, | ||
| "hp": 100, | ||
| "maxHp": 100, | ||
| "gold": 500, | ||
| "exp": 0, | ||
| "trade": [ | ||
| { | ||
| "item": 1, | ||
| "cant": 100 | ||
| }, | ||
| { | ||
| "item": 2, | ||
| "cant": 50 | ||
| }, | ||
| { | ||
| "item": 3, | ||
| "cant": 50 | ||
| } | ||
| ] | ||
| }, | ||
| "2": { | ||
| "name": "Sacerdote", | ||
| "desc": "Cura heridas y resucita a los aventureros caídos", | ||
| "npcType": 2, | ||
| "idHead": 2, | ||
| "idBody": 2, | ||
| "movement": 0, | ||
| "hp": 250, | ||
| "maxHp": 250, | ||
| "gold": 0, | ||
| "exp": 0 | ||
| }, | ||
| "3": { | ||
| "name": "Banquero", | ||
| "desc": "Guarda oro y pertenencias en las bóvedas seguras", | ||
| "npcType": 3, | ||
| "idHead": 3, | ||
| "idBody": 3, | ||
| "movement": 0, | ||
| "hp": 200, | ||
| "maxHp": 200, | ||
| "gold": 10000, | ||
| "exp": 0 | ||
| }, | ||
| "4": { | ||
| "name": "Guardia Real", | ||
| "desc": "Protege las ciudades de criminales y criaturas salvajes", | ||
| "npcType": 4, | ||
| "idHead": 4, | ||
| "idBody": 4, | ||
| "movement": 1, | ||
| "hp": 500, | ||
| "maxHp": 500, | ||
| "minHit": 20, | ||
| "maxHit": 40, | ||
| "def": 25, | ||
| "poderAtaque": 50, | ||
| "poderEvasion": 30, | ||
| "gold": 50, | ||
| "exp": 50 | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| /** | ||
| * Bitcoindefi/OpenAO - Chat Flood Rate Limiter | ||
| */ | ||
| export class ChatRateLimiter { | ||
| private userMessageTimestamps = new Map<string, number[]>(); | ||
|
|
||
| constructor( | ||
| private readonly windowMs: number = 5000, | ||
| private readonly maxMessagesPerWindow: number = 5 | ||
| ) {} | ||
|
|
||
| public canSendMessage(userId: string): { allowed: boolean; remaining: number } { | ||
| const now = Date.now(); | ||
| const timestamps = (this.userMessageTimestamps.get(userId) || []).filter(t => now - t < this.windowMs); | ||
|
|
||
| if (timestamps.length >= this.maxMessagesPerWindow) { | ||
| return { allowed: false, remaining: 0 }; | ||
| } | ||
|
|
||
| timestamps.push(now); | ||
| this.userMessageTimestamps.set(userId, timestamps); | ||
| return { allowed: true, remaining: this.maxMessagesPerWindow - timestamps.length }; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| /** | ||
| * Bitcoindefi/OpenAO - Multi-layer tile opacity & blend mode validator | ||
| */ | ||
| export type BlendMode = 'normal' | 'multiply' | 'screen' | 'overlay'; | ||
|
|
||
| export interface LayerRenderConfig { | ||
| layerIndex: number; | ||
| opacity: number; | ||
| blendMode: BlendMode; | ||
| visible: boolean; | ||
| } | ||
|
|
||
| export function validateLayerRenderConfig(config: Partial<LayerRenderConfig>): LayerRenderConfig { | ||
| const layerIndex = Math.max(0, Math.floor(config.layerIndex ?? 0)); | ||
| const opacity = Math.min(1.0, Math.max(0.0, config.opacity ?? 1.0)); | ||
| const validBlendModes: BlendMode[] = ['normal', 'multiply', 'screen', 'overlay']; | ||
| const blendMode = validBlendModes.includes(config.blendMode as BlendMode) ? (config.blendMode as BlendMode) : 'normal'; | ||
| const visible = config.visible !== false; | ||
|
|
||
| return { layerIndex, opacity, blendMode, visible }; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| /** | ||
| * Bitcoindefi/OpenAO - Trade Escrow Lock | ||
| */ | ||
| export interface TradeSession { | ||
| tradeId: string; | ||
| senderId: string; | ||
| receiverId: string; | ||
| senderItems: Array<{ itemId: number; count: number }>; | ||
| receiverItems: Array<{ itemId: number; count: number }>; | ||
| senderAccepted: boolean; | ||
| receiverAccepted: boolean; | ||
| lockedAt: number; | ||
| } | ||
|
|
||
| export function isTradeReadyForSettlement(session: TradeSession, timeoutMs: number = 30000): boolean { | ||
| if (!session.senderAccepted || !session.receiverAccepted) return false; | ||
| const now = Date.now(); | ||
| if (now - session.lockedAt > timeoutMs) return false; // expired | ||
| return true; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| /** | ||
| * Bitcoindefi/OpenAO - Map NPC Placement & Patrol Generator (Issue #8) | ||
| */ | ||
| export interface NpcPlacementConfig { | ||
| npcId: number; | ||
| mapId: number; | ||
| x: number; | ||
| y: number; | ||
| heading: number; | ||
| patrolRadius?: number; | ||
| } | ||
|
|
||
| export interface PatrolCoordinate { | ||
| x: number; | ||
| y: number; | ||
| step: number; | ||
| } | ||
|
|
||
| export function generatePatrolWaypoints( | ||
| startX: number, | ||
| startY: number, | ||
| radius: number = 3, | ||
| mapBounds: { minX: number; maxX: number; minY: number; maxY: number } = { minX: 1, maxX: 100, minY: 1, maxY: 100 } | ||
| ): PatrolCoordinate[] { | ||
| const waypoints: PatrolCoordinate[] = []; | ||
| const offsets = [ | ||
| { dx: 0, dy: 0 }, | ||
| { dx: radius, dy: 0 }, | ||
| { dx: radius, dy: radius }, | ||
| { dx: 0, dy: radius }, | ||
| { dx: -radius, dy: radius }, | ||
| { dx: -radius, dy: 0 }, | ||
| { dx: -radius, dy: -radius }, | ||
| { dx: 0, dy: -radius }, | ||
| { dx: radius, dy: -radius } | ||
| ]; | ||
|
|
||
| let step = 0; | ||
| for (const off of offsets) { | ||
| const targetX = Math.min(Math.max(startX + off.dx, mapBounds.minX), mapBounds.maxX); | ||
| const targetY = Math.min(Math.max(startY + off.dy, mapBounds.minY), mapBounds.maxY); | ||
| waypoints.push({ x: targetX, y: targetY, step: step++ }); | ||
| } | ||
|
|
||
| return waypoints; | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
userMessageTimestampsretains an entry for every userId that ever sent a message and is never cleaned up, even after a user's timestamps all age out of the window. On a long-running multiplayer server this map grows without bound (the exact buffer saturation the limiter is meant to prevent). Add eviction: after filtering, delete the key when the array is empty, and/or periodically sweep stale entries.Persist the filtered array so stale timestamps don't accumulate; consider a periodic sweep that deletes keys whose arrays are empty to bound memory.:
Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎