Minegame.exe
This commit is contained in:
parent
7b11266f72
commit
11197d1382
6 changed files with 979 additions and 5 deletions
74
website/src/routes/api/gambling/mines/cashout/+server.ts
Normal file
74
website/src/routes/api/gambling/mines/cashout/+server.ts
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { auth } from '$lib/auth';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/db';
|
||||
import { user } from '$lib/server/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { activeGames } from '$lib/server/games/mines';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const POST: RequestHandler = async ({ request }) => {
|
||||
const session = await auth.api.getSession({
|
||||
headers: request.headers
|
||||
});
|
||||
|
||||
if (!session?.user) {
|
||||
throw error(401, 'Not authenticated');
|
||||
}
|
||||
|
||||
try {
|
||||
const { sessionToken } = await request.json();
|
||||
const game = activeGames.get(sessionToken);
|
||||
const userId = Number(session.user.id);
|
||||
|
||||
if (!game) {
|
||||
return json({ error: 'Invalid session' }, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await db.transaction(async (tx) => {
|
||||
const [userData] = await tx
|
||||
.select({ baseCurrencyBalance: user.baseCurrencyBalance })
|
||||
.from(user)
|
||||
.where(eq(user.id, userId))
|
||||
.for('update')
|
||||
.limit(1);
|
||||
|
||||
const currentBalance = Number(userData.baseCurrencyBalance);
|
||||
let payout: number;
|
||||
let newBalance: number;
|
||||
|
||||
// If no tiles revealed, treat as abort and return full bet. This could be changed later to keep the initial bet on the Server
|
||||
if (game.revealedTiles.length === 0) {
|
||||
payout = game.betAmount;
|
||||
newBalance = Math.round((currentBalance + payout) * 100000000) / 100000000;
|
||||
} else {
|
||||
// Calculate payout
|
||||
payout = game.betAmount * game.currentMultiplier;
|
||||
const roundedPayout = Math.round(payout * 100000000) / 100000000;
|
||||
newBalance = Math.round((currentBalance + roundedPayout) * 100000000) / 100000000;
|
||||
}
|
||||
|
||||
await tx
|
||||
.update(user)
|
||||
.set({
|
||||
baseCurrencyBalance: newBalance.toFixed(8),
|
||||
updatedAt: new Date()
|
||||
})
|
||||
.where(eq(user.id, userId));
|
||||
|
||||
activeGames.delete(sessionToken);
|
||||
|
||||
return {
|
||||
newBalance,
|
||||
payout,
|
||||
amountWagered: game.betAmount,
|
||||
isAbort: game.revealedTiles.length === 0
|
||||
};
|
||||
});
|
||||
|
||||
return json(result);
|
||||
} catch (e) {
|
||||
console.error('Mines cashout error:', e);
|
||||
const errorMessage = e instanceof Error ? e.message : 'Internal server error';
|
||||
return json({ error: errorMessage }, { status: 400 });
|
||||
}
|
||||
};
|
||||
81
website/src/routes/api/gambling/mines/reveal/+server.ts
Normal file
81
website/src/routes/api/gambling/mines/reveal/+server.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import { auth } from '$lib/auth';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { activeGames, calculateMultiplier } from '$lib/server/games/mines';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { db } from '$lib/server/db';
|
||||
import { user } from '$lib/server/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
export const POST: RequestHandler = async ({ request }) => {
|
||||
const session = await auth.api.getSession({
|
||||
headers: request.headers
|
||||
});
|
||||
|
||||
if (!session?.user) {
|
||||
throw error(401, 'Not authenticated');
|
||||
}
|
||||
|
||||
try {
|
||||
const { sessionToken, tileIndex } = await request.json();
|
||||
const game = activeGames.get(sessionToken);
|
||||
|
||||
if (!game) {
|
||||
return json({ error: 'Invalid session' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (game.revealedTiles.includes(tileIndex)) {
|
||||
return json({ error: 'Tile already revealed' }, { status: 400 });
|
||||
}
|
||||
|
||||
// Update last activity time
|
||||
game.lastActivity = Date.now();
|
||||
|
||||
// Check if hit mine
|
||||
|
||||
if (game.minePositions.includes(tileIndex)) {
|
||||
game.status = 'lost';
|
||||
const minePositions = game.minePositions;
|
||||
|
||||
// Fetch user balance to return after loss
|
||||
const userId = Number(session.user.id);
|
||||
const [userData] = await db
|
||||
.select({ baseCurrencyBalance: user.baseCurrencyBalance })
|
||||
.from(user)
|
||||
.where(eq(user.id, userId))
|
||||
.limit(1);
|
||||
|
||||
activeGames.delete(sessionToken);
|
||||
|
||||
return json({
|
||||
hitMine: true,
|
||||
minePositions,
|
||||
newBalance: Number(userData.baseCurrencyBalance),
|
||||
status: 'lost'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Safe tile
|
||||
game.revealedTiles.push(tileIndex);
|
||||
game.currentMultiplier = calculateMultiplier(
|
||||
game.revealedTiles.length,
|
||||
game.mineCount,
|
||||
game.betAmount
|
||||
);
|
||||
|
||||
// Check if all safe tiles are revealed. Crazy when you get this :)
|
||||
if (game.revealedTiles.length === 25 - game.mineCount) {
|
||||
game.status = 'won';
|
||||
}
|
||||
|
||||
return json({
|
||||
hitMine: false,
|
||||
currentMultiplier: game.currentMultiplier,
|
||||
status: game.status
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Mines reveal error:', e);
|
||||
const errorMessage = e instanceof Error ? e.message : 'Internal server error';
|
||||
return json({ error: errorMessage }, { status: 400 });
|
||||
}
|
||||
};
|
||||
100
website/src/routes/api/gambling/mines/start/+server.ts
Normal file
100
website/src/routes/api/gambling/mines/start/+server.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import { auth } from '$lib/auth';
|
||||
import { error, json } from '@sveltejs/kit';
|
||||
import { db } from '$lib/server/db';
|
||||
import { user } from '$lib/server/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { activeGames } from '$lib/server/games/mines';
|
||||
import type { RequestHandler } from './$types';
|
||||
|
||||
export const POST: RequestHandler = async ({ request }) => {
|
||||
const session = await auth.api.getSession({
|
||||
headers: request.headers
|
||||
});
|
||||
|
||||
if (!session?.user) {
|
||||
throw error(401, 'Not authenticated');
|
||||
}
|
||||
|
||||
try {
|
||||
const { betAmount, mineCount } = await request.json();
|
||||
const userId = Number(session.user.id);
|
||||
|
||||
if (!betAmount || !mineCount || mineCount < 3 || mineCount > 24) {
|
||||
return json({ error: 'Invalid bet amount or mine count' }, { status: 400 });
|
||||
}
|
||||
|
||||
if (betAmount > 1000000) {
|
||||
return json({ error: 'Bet amount too large' }, { status: 400 });
|
||||
}
|
||||
|
||||
const result = await db.transaction(async (tx) => {
|
||||
const [userData] = await tx
|
||||
.select({ baseCurrencyBalance: user.baseCurrencyBalance })
|
||||
.from(user)
|
||||
.where(eq(user.id, userId))
|
||||
.for('update')
|
||||
.limit(1);
|
||||
|
||||
const currentBalance = Number(userData.baseCurrencyBalance);
|
||||
const roundedAmount = Math.round(betAmount * 100000000) / 100000000;
|
||||
const roundedBalance = Math.round(currentBalance * 100000000) / 100000000;
|
||||
|
||||
if (roundedAmount > roundedBalance) {
|
||||
throw new Error(`Insufficient funds. You need *${roundedAmount.toFixed(2)} but only have *${roundedBalance.toFixed(2)}`);
|
||||
}
|
||||
|
||||
// Generate mine positions
|
||||
const positions = new Set<number>();
|
||||
while (positions.size < mineCount) {
|
||||
positions.add(Math.floor(Math.random() * 25));
|
||||
}
|
||||
const safePositions = [];
|
||||
for (let i = 0; i < 25; i++) {
|
||||
if (!positions.has(i)) safePositions.push(i);
|
||||
}
|
||||
console.log(positions)
|
||||
console.log('Safe positions:', safePositions);
|
||||
|
||||
|
||||
// transaction token for authentication
|
||||
const randomBytes = new Uint8Array(8);
|
||||
crypto.getRandomValues(randomBytes);
|
||||
const sessionToken = Array.from(randomBytes)
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
// Create session
|
||||
activeGames.set(sessionToken, {
|
||||
sessionToken,
|
||||
betAmount: roundedAmount,
|
||||
mineCount,
|
||||
minePositions: Array.from(positions),
|
||||
revealedTiles: [],
|
||||
startTime: now,
|
||||
lastActivity: now,
|
||||
currentMultiplier: 1,
|
||||
status: 'active',
|
||||
userId
|
||||
});
|
||||
|
||||
// Hold bet amount on server to prevent the user from like sending it to another account and farming money without a risk
|
||||
await tx
|
||||
.update(user)
|
||||
.set({
|
||||
baseCurrencyBalance: (roundedBalance - roundedAmount).toFixed(8),
|
||||
updatedAt: new Date()
|
||||
})
|
||||
.where(eq(user.id, userId));
|
||||
|
||||
return { sessionToken };
|
||||
});
|
||||
|
||||
return json(result);
|
||||
} catch (e) {
|
||||
console.error('Mines start error:', e);
|
||||
const errorMessage = e instanceof Error ? e.message : 'Internal server error';
|
||||
return json({ error: errorMessage }, { status: 400 });
|
||||
}
|
||||
};
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts">
|
||||
import Coinflip from '$lib/components/self/games/Coinflip.svelte';
|
||||
import Slots from '$lib/components/self/games/Slots.svelte';
|
||||
import Mines from '$lib/components/self/games/Mines.svelte';
|
||||
import { USER_DATA } from '$lib/stores/user-data';
|
||||
import { PORTFOLIO_SUMMARY, fetchPortfolioSummary } from '$lib/stores/portfolio-data';
|
||||
import { onMount } from 'svelte';
|
||||
|
|
@ -39,14 +40,12 @@
|
|||
|
||||
<SEO
|
||||
title="Gambling - Rugplay"
|
||||
description="Play virtual gambling games with simulated currency in Rugplay. Try coinflip and slots games using virtual money with no real-world value - purely for entertainment."
|
||||
keywords="virtual gambling simulation, coinflip game, slots game, virtual casino, simulated gambling, entertainment games"
|
||||
description="Play virtual gambling games with simulated currency in Rugplay. Try coinflip, slots, and mines games using virtual money with no real-world value - purely for entertainment."
|
||||
keywords="virtual gambling simulation, coinflip game, slots game, mines game, virtual casino, simulated gambling, entertainment games"
|
||||
/>
|
||||
|
||||
<SignInConfirmDialog bind:open={shouldSignIn} />
|
||||
|
||||
|
||||
|
||||
<div class="container mx-auto max-w-4xl p-6">
|
||||
<h1 class="mb-6 text-center text-3xl font-bold">Gambling</h1>
|
||||
|
||||
|
|
@ -54,7 +53,7 @@
|
|||
<div class="flex h-96 items-center justify-center">
|
||||
<div class="text-center">
|
||||
<div class="text-muted-foreground mb-4 text-xl">Sign in to start gambling</div>
|
||||
<p class="text-muted-foreground mb-4 text-sm">You need an account to place bets</p>
|
||||
<p class="text-muted-foreground mb-4 text-sm">You need an account to gamble away your life savings</p>
|
||||
<Button onclick={() => (shouldSignIn = true)}>Sign In</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -73,6 +72,12 @@
|
|||
>
|
||||
Slots
|
||||
</Button>
|
||||
<Button
|
||||
variant={activeGame === 'mines' ? 'default' : 'outline'}
|
||||
onclick={() => (activeGame = 'mines')}
|
||||
>
|
||||
Mines
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- Game Content -->
|
||||
|
|
@ -80,6 +85,8 @@
|
|||
<Coinflip bind:balance onBalanceUpdate={handleBalanceUpdate} />
|
||||
{:else if activeGame === 'slots'}
|
||||
<Slots bind:balance onBalanceUpdate={handleBalanceUpdate} />
|
||||
{:else if activeGame === 'mines'}
|
||||
<Mines bind:balance onBalanceUpdate={handleBalanceUpdate} />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
Reference in a new issue