Skip to content

Commit

Permalink
whitelist (#2571)
Browse files Browse the repository at this point in the history
* whitelist

* whitelist

* fligter

* rever

* Fix/points (#2575)

* fix hs contributions

* Fix points on update

* merge

* Revert "merge"

This reverts commit 252f6a0.

* fix resource arrivals DDOS

---------

Co-authored-by: Bob <[email protected]>
Co-authored-by: aymericdelab <[email protected]>
  • Loading branch information
3 people authored Dec 20, 2024
1 parent f64093c commit ce06e96
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 146 deletions.
16 changes: 15 additions & 1 deletion client/src/dojo/debouncedQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ToriiClient } from "@dojoengine/torii-client";
import debounce from "lodash/debounce";
import {
addArrivalsSubscription,
addHyperstructureSubscription,
addMarketSubscription,
addToSubscription,
addToSubscriptionOneKeyModelbyRealmEntityId,
Expand Down Expand Up @@ -55,7 +56,7 @@ class RequestQueue {
const positionQueue = new RequestQueue();
const subscriptionQueue = new RequestQueue();
const marketQueue = new RequestQueue();

const hyperstructureQueue = new RequestQueue();
// Debounced functions that add to queues
export const debouncedSyncPosition = debounce(
async <S extends Schema>(
Expand Down Expand Up @@ -141,9 +142,22 @@ export const debouncedAddMarketSubscription = debounce(
{ leading: true },
);

export const debouncedAddHyperstructureSubscription = debounce(
async <S extends Schema>(
client: ToriiClient,
components: Component<S, Metadata, undefined>[],
onComplete?: () => void,
) => {
await hyperstructureQueue.add(() => addHyperstructureSubscription(client, components), onComplete);
},
500,
{ leading: true },
);

// Utility function to clear all queues if needed
export const clearAllQueues = () => {
positionQueue.clear();
subscriptionQueue.clear();
marketQueue.clear();
hyperstructureQueue.clear();
};
38 changes: 38 additions & 0 deletions client/src/dojo/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,44 @@ export const addMarketSubscription = async <S extends Schema>(
console.log("MarketEnd", end - start);
};

export const addHyperstructureSubscription = async <S extends Schema>(
client: ToriiClient,
components: Component<S, Metadata, undefined>[],
) => {
const start = performance.now();
await getEntities(
client,
{
Composite: {
operator: "Or",
clauses: [
{
Keys: {
keys: [undefined, undefined],
pattern_matching: "FixedLen",
models: ["s0_eternum-Epoch", "s0_eternum-Progress"],
},
},
{
Keys: {
keys: [undefined, undefined, undefined],
pattern_matching: "FixedLen",
models: ["s0_eternum-Contribution"],
},
},
],
},
},
components as any,
[],
[],
40_000,
false,
);
const end = performance.now();
console.log("HyperstructureEnd", end - start);
};

export const addArrivalsSubscription = async <S extends Schema>(
client: ToriiClient,
components: Component<S, Metadata, undefined>[],
Expand Down
1 change: 1 addition & 0 deletions client/src/dojo/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export async function setup(config: DojoConfig & { state: AppStore }) {
"s0_eternum-Trade",
"s0_eternum-Structure",
"s0_eternum-Battle",
"s0_eternum-Guild",
],
},
},
Expand Down
107 changes: 52 additions & 55 deletions client/src/hooks/helpers/use-resource-arrivals.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import { ContractAddress, ID, Position } from "@bibliothecadao/eternum";
import { useEntityQuery } from "@dojoengine/react";
import {
Entity,
Has,
HasValue,
NotValue,
defineQuery,
getComponentValue,
isComponentUpdate,
runQuery,
} from "@dojoengine/recs";
import { Entity, Has, HasValue, NotValue, defineQuery, getComponentValue, isComponentUpdate } from "@dojoengine/recs";
import { getEntityIdFromKeys } from "@dojoengine/utils";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useDojo } from "../context/DojoContext";
import useNextBlockTimestamp from "../useNextBlockTimestamp";

const DONKEY_RESOURCE_TRACKER = 452312848583266388373324160190187140051835877600158453279131187530910662656n;
const LORDS_RESOURCE_TRACKER = 7237005577332262213973186563042994240829374041602535252466099000494570602496n;

export type ArrivalInfo = {
entityId: ID;
recipientEntityId: ID;
Expand All @@ -23,17 +17,22 @@ export type ArrivalInfo = {
isOwner: boolean;
hasResources: boolean;
isHome: boolean;
// resources: Resource[];
};

const getCurrentDonkeyWeightMinimum = () => {
return Number(localStorage.getItem("WEIGHT_MINIMUM") || 0) * 1000;
};

const usePlayerArrivals = () => {
const {
account: { account },
setup: {
components: { Position, Owner, EntityOwner, OwnedResourcesTracker, ArrivalTime, Weight, Resource, Structure },
components: { Position, Owner, EntityOwner, OwnedResourcesTracker, ArrivalTime, Weight, Structure },
},
} = useDojo();

const minWeight = getCurrentDonkeyWeightMinimum();

const playerStructures = useEntityQuery([
Has(Structure),
HasValue(Owner, { address: ContractAddress(account.address) }),
Expand All @@ -48,68 +47,60 @@ const usePlayerArrivals = () => {

const [entitiesWithInventory, setEntitiesWithInventory] = useState<ArrivalInfo[]>([]);

const queryFragments = [
Has(Weight),
Has(ArrivalTime),
Has(EntityOwner),
NotValue(OwnedResourcesTracker, { resource_types: 0n }),
];

const getArrivalsWithResourceOnPosition = useCallback((positions: Position[]) => {
const arrivals = positions.flatMap((position) => {
return Array.from(runQuery([HasValue(Position, { x: position.x, y: position.y }), ...queryFragments]));
});
return arrivals;
}, []);
const hasMinWeight = useCallback(
(entity: Entity) => {
const weight = getComponentValue(Weight, entity);
return !!(weight?.value && Number(weight.value) >= minWeight);
},
[minWeight],
);

const createArrivalInfo = useCallback(
(id: Entity): ArrivalInfo | undefined => {
// Get required component values
const position = getComponentValue(Position, id);
if (!position) return undefined;

const arrivalTime = getComponentValue(ArrivalTime, id);
if (!arrivalTime) return undefined;

const ownedResourceTracker = getComponentValue(OwnedResourcesTracker, id);
const entityOwner = getComponentValue(EntityOwner, id);
const ownerEntityId = getEntityIdFromKeys([BigInt(entityOwner?.entity_owner_id || 0)]);
const owner = getComponentValue(Owner, ownerEntityId);

if (owner?.address !== ContractAddress(account.address)) {
return undefined;
}
// Return early if missing required components
if (!position || !arrivalTime) return undefined;

const ownedResourceTracker = getComponentValue(OwnedResourcesTracker, id);
// Check if entity has special resource types that don't need weight check
const hasSpecialResources =
ownedResourceTracker?.resource_types === DONKEY_RESOURCE_TRACKER ||
ownedResourceTracker?.resource_types === LORDS_RESOURCE_TRACKER;

// Determine if entity meets weight requirements
const meetsWeightRequirement = hasSpecialResources || hasMinWeight(id);

// Get owner information
const ownerEntityId = getEntityIdFromKeys([BigInt(entityOwner?.entity_owner_id || 0)]);
const owner = getComponentValue(Owner, ownerEntityId);
const isOwner = owner?.address === ContractAddress(account.address);

const hasResources = !!ownedResourceTracker && ownedResourceTracker.resource_types !== 0n;
// Check if entity has resources
const hasResources =
meetsWeightRequirement && !!ownedResourceTracker && ownedResourceTracker.resource_types !== 0n;

// Find matching player structure at position
const playerStructurePosition = playerStructurePositions.find(
(structurePosition) => structurePosition.x === position.x && structurePosition.y === position.y,
);

const isHome = !!playerStructurePosition;

return {
entityId: position.entity_id,
recipientEntityId: playerStructurePosition?.entityId || 0,
arrivesAt: arrivalTime.arrives_at,
isOwner: true,
isOwner,
position: { x: position.x, y: position.y },
hasResources,
isHome,
isHome: !!playerStructurePosition,
};
},
[account, playerStructurePositions],
);

// initial load
useEffect(() => {
const arrivals = getArrivalsWithResourceOnPosition(playerStructurePositions)
.map(createArrivalInfo)
.filter((arrival): arrival is ArrivalInfo => arrival !== undefined)
.filter((arrival) => arrival.hasResources);
setEntitiesWithInventory(arrivals);
}, [playerStructurePositions, getArrivalsWithResourceOnPosition, createArrivalInfo]);

const isMine = useCallback(
(entity: Entity) => {
const entityOwner = getComponentValue(EntityOwner, entity);
Expand All @@ -120,12 +111,21 @@ const usePlayerArrivals = () => {
);

useEffect(() => {
const query = defineQuery([Has(Position), ...queryFragments], { runOnInit: false });
const query = defineQuery(
[
Has(Position),
Has(Weight),
Has(ArrivalTime),
Has(EntityOwner),
NotValue(OwnedResourcesTracker, { resource_types: 0n }),
],
{ runOnInit: false },
);

const handleArrivalUpdate = (arrivals: ArrivalInfo[], newArrival: ArrivalInfo | undefined) => {
if (!newArrival) return arrivals;

if (!newArrival.hasResources || !newArrival.isHome) {
if (!newArrival.hasResources || !newArrival.isHome || !newArrival.isOwner) {
return arrivals.filter((arrival) => arrival.entityId !== newArrival.entityId);
}

Expand All @@ -144,10 +144,7 @@ const usePlayerArrivals = () => {
isComponentUpdate(update, ArrivalTime) ||
isComponentUpdate(update, OwnedResourcesTracker)
) {
const isThisMine = isMine(update.entity);

isThisMine &&
setEntitiesWithInventory((arrivals) => handleArrivalUpdate(arrivals, createArrivalInfo(update.entity)));
setEntitiesWithInventory((arrivals) => handleArrivalUpdate(arrivals, createArrivalInfo(update.entity)));
}
});

Expand Down
1 change: 1 addition & 0 deletions client/src/hooks/helpers/useGuilds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -308,5 +308,6 @@ export const useGuilds = () => {
usePlayerWhitelist,
getGuildFromEntityId,
getPlayersInPlayersGuild,
getPlayerListInGuild,
};
};
7 changes: 6 additions & 1 deletion client/src/ui/components/worldmap/players/PlayerList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,16 @@ export const PlayerList = ({
sort: "none",
});

const sortedPlayers = useMemo(
() => sortItems(players, activeSort, { sortKey: "rank", sort: "asc" }),
[players, activeSort],
);

return (
<div className="flex flex-col h-full p-2 bg-brown-900/50 border border-gold/30 rounded-xl backdrop-blur-sm">
<PlayerListHeader activeSort={activeSort} setActiveSort={setActiveSort} />
<div className="mt-4 overflow-y-auto scrollbar-thin scrollbar-thumb-gold/20 scrollbar-track-transparent">
{sortItems(players, activeSort, { sortKey: "rank", sort: "asc" }).map((player) => (
{sortedPlayers.map((player) => (
<PlayerRow
key={player.address}
player={player}
Expand Down
2 changes: 1 addition & 1 deletion client/src/ui/components/worldmap/players/PlayersPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const PlayersPanel = ({
};
});
return playersWithStructures;
}, [isLoading]);
}, [isLoading, players]);

const filteredPlayers = useMemo(() => {
const term = searchTerm.toLowerCase();
Expand Down
22 changes: 22 additions & 0 deletions client/src/ui/layouts/World.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import useUIStore from "../../hooks/store/useUIStore";

import {
debounceAddResourceArrivals,
debouncedAddHyperstructureSubscription,
debouncedAddMarketSubscription,
debouncedAddToSubscription,
debouncedAddToSubscriptionOneKey,
Expand Down Expand Up @@ -229,6 +230,27 @@ export const World = ({ backgroundImage }: { backgroundImage: string }) => {
fetch();
}, []);

useEffect(() => {
const fetch = async () => {
try {
setLoading(LoadingStateKey.Hyperstructure, true);
console.log("AddToSubscriptionStart - 4");
await Promise.all([
debouncedAddHyperstructureSubscription(dojo.network.toriiClient, dojo.network.contractComponents as any, () =>
setLoading(LoadingStateKey.Hyperstructure, false),
),
]);
} catch (error) {
console.error("Fetch failed", error);
} finally {
// Ensure loading states are reset even if there's an error
setLoading(LoadingStateKey.Hyperstructure, false);
}
};

fetch();
}, []);

const battleViewContent = useMemo(
() => (
<div>
Expand Down
4 changes: 1 addition & 3 deletions client/src/ui/modules/navigation/TopNavigation.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { useGetAllPlayers } from "@/hooks/helpers/use-get-all-players";

import { memo } from "react";
import { Rewards } from "../rewards/Rewards";
import { SettingsWindow } from "../settings/Settings";
import { Social } from "../social/Social";

export const TopMiddleNavigation = memo(() => {
const getPlayers = useGetAllPlayers();
const players = getPlayers();

return (
<>
<div className="pointer-events-auto">
<Social players={players} />
<Social getPlayers={getPlayers} />
<Rewards />
<SettingsWindow />
</div>
Expand Down
Loading

0 comments on commit ce06e96

Please sign in to comment.