Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/aws_dev_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ jobs:
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
with:
mask-password: 'false'

- name: Image Metadata
id: metadata
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/aws_prod_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ jobs:
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
with:
mask-password: 'false'

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
Expand Down
2 changes: 1 addition & 1 deletion client/src/context/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const globalReducer = (state, action) => {
...state,
checkpointsCompleted: payload.checkpointsCompleted,
elapsedTimeInSeconds: payload.elapsedTimeInSeconds,
highscore: payload.highscore,
highScore: payload.highScore,
isAdmin: payload.isAdmin,
leaderboard: payload.leaderboard,
numberOfCheckpoints: payload.numberOfCheckpoints,
Expand Down
41 changes: 13 additions & 28 deletions client/src/pages/Leaderboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { backendAPI, loadGameState } from "@utils";

function Leaderboard() {
const dispatch = useContext(GlobalDispatchContext);
const { leaderboard, highscore, isAdmin } = useContext(GlobalStateContext);
const { leaderboard, highScore, isAdmin } = useContext(GlobalStateContext);
const [loading, setLoading] = useState(true);
const [showSettings, setShowSettings] = useState(false);
const location = useLocation();
Expand All @@ -36,23 +36,6 @@ function Leaderboard() {
fetchGameState();
}, [dispatch, backendAPI]);

const sortedLeaderboard = leaderboard
? Object.entries(leaderboard)
.filter(([, playerData]) => playerData && playerData.highscore)
.sort(([, a], [, b]) => {
const aScore = a.highscore.split(":").map(Number);
const bScore = b.highscore.split(":").map(Number);

for (let i = 0; i < 3; i++) {
if (aScore[i] !== bScore[i]) {
return aScore[i] - bScore[i];
}
}
return 0;
})
.slice(0, 20)
: [];

if (loading) return <Loading />;

if (showSettings) return <AdminView setShowSettings={setShowSettings} />;
Expand All @@ -63,10 +46,10 @@ function Leaderboard() {
<>
{isAdmin && <AdminGear setShowSettings={setShowSettings} />}
<div className="px-4 my-6">
<div className="highscore-container">
<div className="highScore-container">
<div className="icon">🏅</div>
<h3>Personal Best</h3>
<p>{highscore || "No highscore available"}</p>
<p>{highScore || "No highScore available"}</p>
</div>
<div className="icon pt-4">🏆</div>
<div className="pb-4">
Expand All @@ -81,18 +64,20 @@ function Leaderboard() {
</tr>
</thead>
<tbody>
{sortedLeaderboard?.length === 0 ? (
{leaderboard?.length === 0 ? (
<tr>
<td colSpan="3">There are no race finishes yet.</td>
</tr>
) : (
sortedLeaderboard?.map(([userId, entry], index) => (
<tr key={userId}>
<td>{index + 1}</td>
<td>{entry.username}</td>
<td>{entry.highscore}</td>
</tr>
))
leaderboard?.map((item, index) => {
return (
<tr key={index}>
<td>{index + 1}</td>
<td>{item.displayName}</td>
<td>{item.highScore}</td>
</tr>
);
})
)}
</tbody>
</table>
Expand Down
2 changes: 1 addition & 1 deletion client/src/pages/Leaderboard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
text-align: center;
}

.highscore-container {
.highScore-container {
text-align: center;
margin-bottom: 20px;
padding: 10px;
Expand Down
2 changes: 1 addition & 1 deletion client/src/utils/loadGameState.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const loadGameState = async (dispatch) => {
payload: {
checkpointsCompleted: result.data.checkpointsCompleted,
elapsedTimeInSeconds: result.data.elapsedTimeInSeconds,
highscore: result.data.highscore,
highScore: result.data.highScore,
isAdmin: result.data.isAdmin,
leaderboard: result.data.leaderboard,
numberOfCheckpoints: result.data.numberOfCheckpoints,
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "",
"dependencies": {
"@googleapis/sheets": "^7.0.0",
"@rtsdk/topia": "^0.15.8",
"@rtsdk/topia": "^0.17.4",
"axios": "^1.6.7",
"body-parser": "^1.20.2",
"concurrently": "^8.2.2",
Expand Down
7 changes: 7 additions & 0 deletions server/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,10 @@ export const TRACKS = [
sceneId: "oXghmgohNPuaICPA9Ne5",
},
];

export const DEFAULT_PROGRESS = {
checkpoints: { 0: false },
elapsedTime: null,
highScore: null,
startTimestamp: null,
};
26 changes: 13 additions & 13 deletions server/controllers/handleCancelRace.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { errorHandler, getCredentials, World } from "../utils/index.js";
import { errorHandler, getCredentials, getVisitor, updateVisitorProgress } from "../utils/index.js";

export const handleCancelRace = async (req, res) => {
try {
const credentials = getCredentials(req.query);
const { urlSlug, profileId, sceneDropId } = credentials;

const world = await World.create(urlSlug, { credentials });
const { visitor, visitorProgress } = await getVisitor(credentials);

if (profileId) {
world
.updateDataObject({
[`${sceneDropId}.profiles.${profileId}.checkpoints`]: {},
[`${sceneDropId}.profiles.${profileId}.elapsedTime`]: null,
[`${sceneDropId}.profiles.${profileId}.startTimestamp`]: null,
})
.then()
.catch((error) => console.error(JSON.stringify(error)));
}
const updateVisitorResult = await updateVisitorProgress({
credentials,
updatedProgress: {
checkpoints: {},
elapsedTime: null,
startTimestamp: null,
},
visitor,
visitorProgress,
});
if (updateVisitorResult instanceof Error) throw updateVisitorResult;

return res.json({ success: true });
} catch (error) {
Expand Down
37 changes: 21 additions & 16 deletions server/controllers/handleCheckpointEntered.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
import {
getCredentials,
World,
errorHandler,
checkpointZeroEntered,
finishLineEntered,
checkpointEntered,
Visitor,
getVisitor,
getElapsedTime,
} from "../utils/index.js";
import { CHECKPOINT_NAMES } from "../constants.js";
import redisObj from "../redis/redis.js";

export const handleCheckpointEntered = async (req, res) => {
try {
const credentials = getCredentials(req.body);
const { profileId, sceneDropId, urlSlug, visitorId } = credentials;
const { profileId, urlSlug, visitorId } = credentials;
const { uniqueName } = req.body;
const channel = `${process.env.INTERACTIVE_KEY}_RACE`;

const { visitorProgress } = await getVisitor(credentials);
const { startTimestamp } = visitorProgress;

const currentTimestamp = Date.now();
const currentElapsedTime = getElapsedTime(currentTimestamp, startTimestamp);
const checkpointNumber = uniqueName === CHECKPOINT_NAMES.START ? 0 : parseInt(uniqueName.split("-").pop(), 10);

if (!startTimestamp) return { success: false, message: "Race has not started yet" };

const cachedCheckpoints = JSON.parse(await redisObj.get(profileId)) || {};

if (checkpointNumber !== 0) {
const cachedCheckpoints = JSON.parse(await redisObj.get(profileId)) || {};
if (checkpointNumber > 1 && !cachedCheckpoints[checkpointNumber - 2]) {
redisObj.publish(channel, {
profileId,
Expand Down Expand Up @@ -52,25 +60,22 @@ export const handleCheckpointEntered = async (req, res) => {
}
}

const world = World.create(urlSlug, { credentials });
const dataObject = await world.fetchDataObject();
const raceObject = dataObject?.[sceneDropId] || {};
const profileObject = raceObject?.profiles?.[profileId] || {};

const { startTimestamp } = profileObject;
if (!startTimestamp) return { success: false, message: "Race has not started yet" };
const currentTimestamp = Date.now();

if (checkpointNumber === 0) {
const currentElapsedTime = checkpointZeroEntered(currentTimestamp, profileObject);
redisObj.publish(channel, {
profileId,
checkpointNumber,
currentRaceFinishedElapsedTime: currentElapsedTime,
});
await finishLineEntered({ credentials, currentElapsedTime, profileObject, raceObject, world });
const result = await finishLineEntered({ credentials, currentElapsedTime });
if (result instanceof Error) throw result;
} else {
await checkpointEntered({ checkpointNumber, currentTimestamp, credentials, profileObject, world });
const result = await checkpointEntered({
checkpoints: cachedCheckpoints,
checkpointNumber,
currentElapsedTime,
credentials,
});
if (result instanceof Error) throw result;
}

return res.status(200).json({ success: true });
Expand Down
9 changes: 3 additions & 6 deletions server/controllers/handleCompleteRace.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { World, errorHandler, getCredentials } from "../utils/index.js";
import { errorHandler, getCredentials, getVisitor } from "../utils/index.js";

export const handleCompleteRace = async (req, res) => {
try {
const credentials = getCredentials(req.query);
const { urlSlug, profileId } = credentials;

const world = await World.create(urlSlug, { credentials });
await world.fetchDataObject();

const elapsedTime = world?.dataObject?.sceneDropId?.profiles?.[profileId]?.elapsedTime;
const { visitorProgress } = await getVisitor(credentials);
const elapsedTime = visitorProgress.elapsedTime;

return res.json({ success: true, elapsedTime });
} catch (error) {
Expand Down
Loading