Skip to content

Commit

Permalink
Merge pull request #275 from supreme2580/live-websockets
Browse files Browse the repository at this point in the history
Live websockets
  • Loading branch information
b-j-roberts authored Jan 8, 2025
2 parents a095ef6 + 5253d01 commit 9627722
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 36 deletions.
14 changes: 7 additions & 7 deletions backend/config/rounds.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import (
)

type Round3 struct {
Width uint `json:"width"`
Height uint `json:"height"`
Timer uint `json:"timer"`
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
Width uint `json:"width"`
Height uint `json:"height"`
Timer uint `json:"timer"`
StartTime uint `json:"startTime"`
EndTime uint `json:"endTime"`
}

type RoundsConfig struct {
Expand All @@ -22,8 +22,8 @@ var DefaultRoundsConfig = &RoundsConfig{
Width: 256,
Height: 192,
Timer: 5,
StartTime: "2024-12-01T00:00:00Z",
EndTime: "2025-01-01T00:00:00Z",
StartTime: 0,
EndTime: 3000000000000,
},
}

Expand Down
18 changes: 18 additions & 0 deletions backend/routes/indexer/worlds.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,24 @@ func processCanvasCreatedEvent(event IndexerEvent) {
PrintIndexerError("processCanvasCreatedEvent", "Failed to encode image", filename, err)
return
}

// After world creation
var message = map[string]interface{}{
"messageType": "newWorld",
"worldId": canvasId,
"world": map[string]interface{}{
"worldId": canvasId,
"host": host,
"name": name,
"uniqueName": uniqueName,
"width": width,
"height": height,
"timeBetweenPixels": timeBetweenPixels,
"startTime": startTime,
"endTime": endTime,
},
}
routeutils.SendWebSocketMessage(message)
}

func revertCanvasCreatedEvent(event IndexerEvent) {
Expand Down
26 changes: 26 additions & 0 deletions backend/routes/worlds.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func InitWorldsRoutes() {
http.HandleFunc("/get-world-id", getWorldId)
http.HandleFunc("/get-world", getWorld)
http.HandleFunc("/get-worlds", getWorlds)
http.HandleFunc("/get-home-worlds", getHomeWorlds)
http.HandleFunc("/get-new-worlds", getNewWorlds)
http.HandleFunc("/get-favorite-worlds", getFavoriteWorlds)
// TODO: Hot/top use user interactivity instead of favorite count
Expand Down Expand Up @@ -151,6 +152,31 @@ func getWorlds(w http.ResponseWriter, r *http.Request) {
routeutils.WriteDataJson(w, string(worlds))
}

func getHomeWorlds(w http.ResponseWriter, r *http.Request) {
// TODO: Top compute
address := r.URL.Query().Get("address")
if address == "" {
address = "0"
}

roundConfig := core.ArtPeaceBackend.RoundsConfig.Round3

query := `
SELECT
*
FROM
worlds
WHERE width = $1 AND height = $2 AND time_between_pixels = $3 AND start_time = TO_TIMESTAMP($4) AND end_time = TO_TIMESTAMP($5)
ORDER BY worlds.world_id DESC
LIMIT 13`
worlds, err := core.PostgresQueryJson[WorldData](query, roundConfig.Width, roundConfig.Height, roundConfig.Timer, roundConfig.StartTime, roundConfig.EndTime)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to retrieve Worlds")
return
}
routeutils.WriteDataJson(w, string(worlds))
}

func getNewWorlds(w http.ResponseWriter, r *http.Request) {
address := r.URL.Query().Get("address")
if address == "" {
Expand Down
4 changes: 2 additions & 2 deletions configs/canvas.config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"canvas": {
"width": 1024,
"height": 768
"width": 518,
"height": 396
},
"colors": [
"FAFAFA",
Expand Down
4 changes: 2 additions & 2 deletions configs/rounds.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"width": 256,
"height": 192,
"timer": 5,
"startTime": "2024-12-06T00:00:00Z",
"endTime": "2025-01-01T00:00:00Z"
"startTime": 1734654796,
"endTime": 1737333196
}
}
97 changes: 80 additions & 17 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,59 @@ import Hamburger from './resources/icons/Hamburger.png';

function App() {
const worldsMode = true;
const [openedWorldId, setOpenedWorldId] = useState(null);
const [openedWorldId, setOpenedWorldId] = useState(worldsMode ? 0 : null);
const [activeWorld, setActiveWorld] = useState(null);
const [surroundingWorlds, setSurroundingWorlds] = useState([]);

// Page management
useEffect(() => {
const getWorldId = async () => {
let currentWorldId = 0;

if (location.pathname.startsWith('/worlds/')) {
let worldSlug = location.pathname.split('/worlds/')[1];
let response = await fetchWrapper(
`get-world-id?worldName=${worldSlug}`
);

if (response.data === undefined || response.data === null) {
setActiveWorld(null);
setOpenedWorldId(null);
return;
setOpenedWorldId(0);
} else {
setActiveWorld(response.data);
setOpenedWorldId(response.data);
currentWorldId = response.data;
}
} else {
const response = await fetchWrapper('get-world?worldId=0');
if (response.data) {
setActiveWorld(response.data);
}
setOpenedWorldId(0);
}

setOpenedWorldId(response.data);
// Always fetch surrounding worlds
const surroundingResponse = await fetchWrapper('get-home-worlds');
if (surroundingResponse.data) {
const otherWorlds = surroundingResponse.data
.filter((world) => world.worldId !== currentWorldId)
.slice(0, 12);

// Pad array with null values if less than 12 worlds
let paddedWorlds = [...otherWorlds];
while (paddedWorlds.length < 12) {
paddedWorlds.push(null);
}
if (paddedWorlds.length > 12) {
paddedWorlds = paddedWorlds.slice(0, 12);
}

setSurroundingWorlds(paddedWorlds);
} else {
setActiveWorld(null);
setOpenedWorldId(null);
setSurroundingWorlds(Array(12).fill(null)); // Fill with 12 null values if no worlds found
}
};

getWorldId();
}, [location.pathname]);

Expand Down Expand Up @@ -293,22 +323,42 @@ function App() {
useEffect(() => {
const processMessage = async (message) => {
if (message) {
// Check the message type and handle accordingly
if (message.messageType === 'colorPixel') {
if (message.color >= colors.length) {
// Get new colors from backend
await fetchColors();
}
colorPixel(message.position, message.color);
} else if (message.messageType === 'colorWorldPixel') {
if (message.worldId.toString() !== openedWorldId) {
return;
}
if (message.color >= colors.length) {
// Get new colors from backend
await fetchColors();
if (message.worldId.toString() === openedWorldId.toString()) {
if (message.color >= colors.length) {
await fetchColors();
}
colorPixel(message.position, message.color);
}
colorPixel(message.position, message.color);

surroundingWorlds.forEach((world) => {
if (
world &&
world.worldId.toString() === message.worldId.toString()
) {
const surroundingCanvas = document.querySelector(
`canvas[data-world-id="${world.worldId}"]`
);
if (surroundingCanvas) {
const context = surroundingCanvas.getContext('2d');
const canvasWidth = surroundingCanvas[world.worldId].width;
const x = message.position % canvasWidth;
const y = Math.floor(message.position / canvasWidth);
const canvasColors = surroundingCanvas[world.worldId].colors;
const colorHex = `#${canvasColors[message.color]}FF`;
context.fillStyle = colorHex;
context.fillRect(x, y, 1, 1);
}
}
});
} else if (message.messageType === 'newWorld') {
// TODO
setOpenedWorldId(message.worldId);
} else if (
message.messageType === 'nftMinted' &&
activeTab === 'NFTs'
Expand All @@ -321,7 +371,7 @@ function App() {
};

processMessage(lastJsonMessage);
}, [lastJsonMessage]);
}, [lastJsonMessage, openedWorldId, colors]);

// Canvas
const [width, setWidth] = useState(canvasConfig.canvas.width);
Expand All @@ -335,6 +385,12 @@ function App() {
const context = canvas.getContext('2d');
const x = position % width;
const y = Math.floor(position / width);

if (x < 0 || x >= width || y < 0 || y >= height) {
console.error('Invalid pixel position:', x, y);
return;
}

const colorIdx = color;
const colorHex = `#${colors[colorIdx]}FF`;
context.fillStyle = colorHex;
Expand Down Expand Up @@ -1273,9 +1329,16 @@ function App() {
setIsEraserMode={setIsEraserMode}
clearExtraPixel={clearExtraPixel}
setLastPlacedTime={setLastPlacedTime}
surroundingWorlds={surroundingWorlds}
/>
{(!isMobile || activeTab === tabs[0]) && (
<div className='App__logo'>
<div
className='App__logo'
onClick={() => {
setActiveTab(tabs[0]);
window.location.pathname = '/';
}}
>
<img
src={logo}
alt='logo'
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/configs/canvas.config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"canvas": {
"width": 1024,
"height": 768
"width": 518,
"height": 396
},
"colors": [
"FAFAFA",
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/tabs/worlds/WorldsCreationPanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,7 @@
);
width: 100;
}

.WorldsCreationPanel__competition__value {
padding-left: 1rem;
}
14 changes: 8 additions & 6 deletions frontend/src/tabs/worlds/WorldsCreationPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ const WorldsCreationPanel = (props) => {
maxFee
}
);
console.log(result);
// TODO: Update the UI with the new World
console.log('World creation result:', result); // Debug log

closePanel();
props.setActiveTab('Worlds');
};
Expand Down Expand Up @@ -213,8 +213,11 @@ const WorldsCreationPanel = (props) => {
submitStart,
submitEnd
);

window.location.href = `/worlds/${worldSlug}`;
return;
}

const host = '0x' + props.queryAddress;
let createWorldEndpoint = 'create-canvas-devnet';
const response = await fetchWrapper(createWorldEndpoint, {
Expand All @@ -234,8 +237,7 @@ const WorldsCreationPanel = (props) => {
});
if (response.result) {
console.log(response.result);
closePanel();
props.setActiveTab('Worlds');
window.location.href = `/worlds/${worldSlug}`;
}
};

Expand Down Expand Up @@ -292,13 +294,13 @@ const WorldsCreationPanel = (props) => {

const getCompetitionStart = () => {
return competitionConfig?.startTime
? new Date(competitionConfig.startTime).getTime()
? new Date(competitionConfig.startTime * 1000).getTime()
: new Date('2024-12-07T00:00:00Z').getTime();
};

const getCompetitionEnd = () => {
return competitionConfig?.endTime
? new Date(competitionConfig.endTime).getTime()
? new Date(competitionConfig.endTime * 1000).getTime()
: new Date('2025-01-02T00:00:00Z').getTime();
};

Expand Down

0 comments on commit 9627722

Please sign in to comment.