diff --git a/src/database/index.js b/src/database/index.js index 374b353..0f00aa6 100644 --- a/src/database/index.js +++ b/src/database/index.js @@ -603,9 +603,9 @@ export const getAllAkhys = flopoDB.prepare( ----------------------------*/ export const insertSkin = flopoDB.prepare( `INSERT INTO skins (uuid, displayName, contentTierUuid, displayIcon, user_id, tierRank, tierColor, tierText, - basePrice, currentLvl, currentChroma, currentPrice, maxPrice) + basePrice, maxPrice) VALUES (@uuid, @displayName, @contentTierUuid, @displayIcon, @user_id, @tierRank, @tierColor, @tierText, - @basePrice, @currentLvl, @currentChroma, @currentPrice, @maxPrice)`, + @basePrice, @maxPrice)`, ); export const updateSkin = flopoDB.prepare( `UPDATE skins diff --git a/src/server/routes/api.js b/src/server/routes/api.js index 17394a3..36fd3ec 100644 --- a/src/server/routes/api.js +++ b/src/server/routes/api.js @@ -9,6 +9,7 @@ import { getMarketOffersBySkin, getOfferBids, getSkin, + getAllAvailableSkins, getUser, getUserElo, getUserGames, @@ -116,14 +117,60 @@ export function apiRoutes(client, io) { router.get("/carousel-skins", (req, res) => { try { + const dbSkins = getAllAvailableSkins.all(); const filteredSkins = skins.filter( - (s) => s.displayIcon !== null && s.displayName.toLowerCase().includes("champions"), + (s) => dbSkins.find((dbSkin) => dbSkin.uuid === s.uuid), ); filteredSkins.forEach((s) => { - let dbSKin = getSkin.get(s.uuid); - s.tierColor = dbSKin?.tierColor; + let dbSkin = getSkin.get(s.uuid); + s.tierColor = dbSkin?.tierColor; }); - res.json(filteredSkins); + const tierWeights = { + "12683d76-48d7-84a3-4e09-6985794f0445": 50, // Select + "0cebb8be-46d7-c12a-d306-e9907bfc5a25": 30, // Deluxe + "60bca009-4182-7998-dee7-b8a2558dc369": 15, // Premium + "e046854e-406c-37f4-6607-19a9ba8426fc": 4, // Exclusive + "411e4a55-4e59-7757-41f0-86a53f101bb5": 1, // Ultra + } + filteredSkins.forEach((s) => { + s.weight = tierWeights[s.tierUuid] ?? 1; // fallback if missing + }); + + function weightedSample(arr, count) { + let totalWeight = arr.reduce((sum, x) => sum + x.weight, 0); + const list = [...arr]; + const result = []; + + for (let i = 0; i < count && list.length > 0; i++) { + let r = Math.random() * totalWeight; + let running = 0; + let pickIndex = -1; + + for (let j = 0; j < list.length; j++) { + running += list[j].weight; + if (r <= running) { + pickIndex = j; + break; + } + } + + if (pickIndex < 0) break; + + const picked = list.splice(pickIndex, 1)[0]; + result.push(picked); + + // Subtract removed weight + totalWeight -= picked.weight; + } + + return result; + } + + const selectedSkins = weightedSample(filteredSkins, 100); + + const randomSelectedSkinIndex = Math.floor(Math.random() * selectedSkins.length); + const randomSelectedSkinUuid = selectedSkins[randomSelectedSkinIndex].uuid; + res.json({ selectedSkins, randomSelectedSkinUuid, randomSelectedSkinIndex }); } catch (error) { console.error("Error fetching skins:", error); res.status(500).json({ error: "Failed to fetch skins." }); diff --git a/src/server/routes/market.js b/src/server/routes/market.js index 6807648..03e117f 100644 --- a/src/server/routes/market.js +++ b/src/server/routes/market.js @@ -13,6 +13,7 @@ import { getUser, insertBid, insertLog, + insertMarketOffer, updateUserCoins, } from "../../database/index.js"; import { emitMarketUpdate } from "../socket.js"; @@ -68,12 +69,35 @@ export function marketRoutes(client, io) { }); router.post("/place-offer", async (req, res) => { + const { seller_id, skin_uuid, starting_price, delay, duration, timestamp } = req.body; + const now = Date.now(); try { + const skin = getSkin.get(skin_uuid); + if (!skin) return res.status(404).send({ error: "Skin not found" }); + const seller = getUser.get(seller_id); + if (!seller) return res.status(404).send({ error: "Seller not found" }); + if (skin.user_id !== seller.id) return res.status(403).send({ error: "You do not own this skin" }); + + const opening_at = now + delay; + const closing_at = opening_at + duration; + + insertMarketOffer.run({ + id: Date.now() + '-' + seller.id + '-' + skin.uuid, + skin_uuid: skin.uuid, + seller_id: seller.id, + starting_price: starting_price, + buyout_price: null, + status: delay > 0 ? "pending" : "open", + opening_at: opening_at, + closing_at: closing_at, + }); // Placeholder for placing an offer logic // Extract data from req.body and process accordingly - res.status(200).send({ message: "Offer placed successfully" }); + await emitMarketUpdate(); + res.status(200).send({ message: "Offre créée avec succès" }); } catch (e) { - res.status(500).send({ error: e }); + console.log(e) + return res.status(500).send({ error: e }); } }); diff --git a/src/utils/index.js b/src/utils/index.js index 2f0f531..67b0edc 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -21,6 +21,7 @@ import { updateUserCoins, } from "../database/index.js"; import { activeInventories, activePredis, activeSearchs, pokerRooms, skins } from "../game/state.js"; +import { emitMarketUpdate } from "../server/socket.js"; export async function InstallGlobalCommands(appId, commands) { // API endpoint to overwrite global commands @@ -119,7 +120,7 @@ export async function getAkhys(client) { */ export function setupCronJobs(client, io) { // Every 5 minutes: Update market offers - cron.schedule("*/1 * * * *", () => { + cron.schedule("* * * * *", () => { console.log("[Cron] Checking market offers for updates..."); handleMarketOffersUpdate(); }); @@ -258,38 +259,55 @@ export async function postAPOBuy(userId, amount) { function handleMarketOffersUpdate() { const now = Date.now(); const offers = getMarketOffers.all(); - offers.forEach((offer) => { - console.log(`[Market Cron] Checking offer ID: ${offer.id}, Status: ${offer.status}`); - + offers.forEach(async (offer) => { + if (now >= offer.opening_at && offer.status === "pending") { + updateMarketOffer.run({ id: offer.id, final_price: null, buyer_id: null, status: "open" }); + //TODO: Maybe notify seller that their offer is now open + await emitMarketUpdate(); + } if (now >= offer.closing_at && offer.status !== "closed") { const bids = getOfferBids.all(offer.id); - const lastBid = bids[0]; - const seller = getUser.get(offer.seller_id); - const buyer = getUser.get(lastBid.bidder_id); - try { - // Change skin ownership - const skin = getSkin.get(offer.skin_uuid); - if (!skin) throw new Error(`Skin not found for offer ID: ${offer.id}`); - updateSkin.run({ - user_id: buyer.id, - currentLvl: skin.currentLvl, - currentChroma: skin.currentChroma, - currentPrice: skin.currentPrice, - uuid: skin.uuid, - }); + if (bids.length === 0) { + // No bids placed, mark as closed without a sale updateMarketOffer.run({ id: offer.id, - buyer_id: buyer.id, - final_price: lastBid.offer_amount, + buyer_id: null, + final_price: null, status: "closed", }); - const newUserCoins = seller.coins + lastBid.offer_amount; - updateUserCoins.run({ id: seller.id, coins: newUserCoins }); - //TODO: Notify users in DMs - } catch (e) { - console.error(`[Market Cron] Error processing offer ID: ${offer.id}`, e); + await emitMarketUpdate(); + } else { + const lastBid = bids[0]; + const seller = getUser.get(offer.seller_id); + const buyer = getUser.get(lastBid.bidder_id); + + try { + // Change skin ownership + const skin = getSkin.get(offer.skin_uuid); + if (!skin) throw new Error(`Skin not found for offer ID: ${offer.id}`); + updateSkin.run({ + user_id: buyer.id, + currentLvl: skin.currentLvl, + currentChroma: skin.currentChroma, + currentPrice: skin.currentPrice, + uuid: skin.uuid, + }); + updateMarketOffer.run({ + id: offer.id, + buyer_id: buyer.id, + final_price: lastBid.offer_amount, + status: "closed", + }); + const newUserCoins = seller.coins + lastBid.offer_amount; + updateUserCoins.run({ id: seller.id, coins: newUserCoins }); + //TODO: Notify users in DMs + await emitMarketUpdate(); + } catch (e) { + console.error(`[Market Cron] Error processing offer ID: ${offer.id}`, e); + } } + } }); }