Add multiplayer rooms and Vercel analytics
This commit is contained in:
@@ -43,6 +43,93 @@ export type Database = {
|
||||
};
|
||||
public: {
|
||||
Tables: {
|
||||
multiplayer_rooms: {
|
||||
Row: {
|
||||
id: string;
|
||||
revision_id: string;
|
||||
host_player_id: string;
|
||||
difficulty: string;
|
||||
player_limit: number;
|
||||
status: string;
|
||||
current_round: number;
|
||||
transition_at: string | null;
|
||||
created_at: string;
|
||||
};
|
||||
Insert: {
|
||||
id?: string;
|
||||
revision_id: string;
|
||||
host_player_id: string;
|
||||
difficulty: string;
|
||||
player_limit: number;
|
||||
status?: string;
|
||||
current_round?: number;
|
||||
transition_at?: string | null;
|
||||
created_at?: string;
|
||||
};
|
||||
Update: {
|
||||
host_player_id?: string;
|
||||
status?: string;
|
||||
current_round?: number;
|
||||
transition_at?: string | null;
|
||||
};
|
||||
Relationships: [];
|
||||
};
|
||||
multiplayer_rounds: {
|
||||
Row: {
|
||||
room_id: string;
|
||||
round_number: number;
|
||||
viewpoint: Json;
|
||||
deadline: string | null;
|
||||
closed_at: string | null;
|
||||
};
|
||||
Insert: {
|
||||
room_id: string;
|
||||
round_number: number;
|
||||
viewpoint: Json;
|
||||
deadline?: string | null;
|
||||
closed_at?: string | null;
|
||||
};
|
||||
Update: { deadline?: string | null; closed_at?: string | null };
|
||||
Relationships: [];
|
||||
};
|
||||
multiplayer_players: {
|
||||
Row: {
|
||||
id: string;
|
||||
room_id: string;
|
||||
access_hash: string;
|
||||
display_name: string;
|
||||
ready_round: number;
|
||||
has_left: boolean;
|
||||
joined_at: string;
|
||||
};
|
||||
Insert: {
|
||||
id?: string;
|
||||
room_id: string;
|
||||
access_hash: string;
|
||||
display_name: string;
|
||||
ready_round?: number;
|
||||
has_left?: boolean;
|
||||
joined_at?: string;
|
||||
};
|
||||
Update: { ready_round?: number; has_left?: boolean };
|
||||
Relationships: [];
|
||||
};
|
||||
multiplayer_guesses: {
|
||||
Row: {
|
||||
room_id: string;
|
||||
player_id: string;
|
||||
round_number: number;
|
||||
result: Json;
|
||||
};
|
||||
Insert: {
|
||||
room_id: string;
|
||||
player_id: string;
|
||||
round_number: number;
|
||||
result: Json;
|
||||
};
|
||||
Update: { result?: Json };
|
||||
Relationships: [];
|
||||
};
|
||||
site_settings: {
|
||||
Row: {
|
||||
id: boolean;
|
||||
@@ -540,6 +627,25 @@ export type Database = {
|
||||
[_ in never]: never;
|
||||
};
|
||||
Functions: {
|
||||
create_multiplayer_room: {
|
||||
Args: {
|
||||
p_dataset: string;
|
||||
p_difficulty: string;
|
||||
p_name: string;
|
||||
p_limit: number;
|
||||
p_access_hash: string;
|
||||
};
|
||||
Returns: string;
|
||||
};
|
||||
multiplayer_action: {
|
||||
Args: {
|
||||
p_room: string;
|
||||
p_access_hash: string;
|
||||
p_action: string;
|
||||
p_payload: Json;
|
||||
};
|
||||
Returns: Json;
|
||||
};
|
||||
legacy_password_needed: { Args: { p_user: string }; Returns: boolean };
|
||||
claim_legacy_password: { Args: { p_user: string }; Returns: boolean };
|
||||
set_beta_badge: {
|
||||
|
||||
@@ -605,5 +605,64 @@
|
||||
"Not now": "Не сейчас",
|
||||
"Select the password and copy it manually.": "Выдели пароль и скопируй его вручную.",
|
||||
"A password is already set or was already issued. Use password recovery if needed.": "Пароль уже установлен или был выдан ранее. Если нужно, воспользуйся восстановлением пароля.",
|
||||
"Password setup could not be confirmed. Use password recovery before signing out.": "Не удалось подтвердить установку пароля. Восстанови пароль перед выходом из аккаунта."
|
||||
"Password setup could not be confirmed. Use password recovery before signing out.": "Не удалось подтвердить установку пароля. Восстанови пароль перед выходом из аккаунта.",
|
||||
"Game mode": "Режим игры",
|
||||
"Solo": "Одиночная игра",
|
||||
"Multiplayer": "Мультиплеер",
|
||||
"Your player name": "Ваше игровое имя",
|
||||
"Enter a player name": "Введите игровое имя",
|
||||
"Player limit (including you)": "Лимит игроков (включая вас)",
|
||||
"Create a room, share the invite link, and start when your friends arrive.": "Создайте комнату, поделитесь ссылкой и начните игру, когда друзья присоединятся.",
|
||||
"Create multiplayer room": "Создать комнату",
|
||||
"Retry": "Повторить",
|
||||
"Loading multiplayer room…": "Загрузка комнаты…",
|
||||
"Final standings": "Итоговая таблица",
|
||||
"This room is closed": "Эта комната закрыта",
|
||||
"An expedition with friends.": "Экспедиция с друзьями.",
|
||||
"Join your friends for five rounds on the same map.": "Присоединитесь к друзьям: пять раундов на одной карте.",
|
||||
"{count} / {limit} players": "Игроков: {count} / {limit}",
|
||||
"This room is full": "Комната заполнена",
|
||||
"Join room": "Войти в комнату",
|
||||
"This match has already started": "Игра уже началась",
|
||||
"Share the invite link. Start when your friends arrive.": "Поделитесь ссылкой. Начните игру, когда друзья присоединятся.",
|
||||
"You’re in! Waiting for the room owner to start.": "Вы в комнате! Ждём, когда владелец начнёт игру.",
|
||||
"Invite link": "Ссылка-приглашение",
|
||||
"Copied!": "Скопировано!",
|
||||
"Copy link": "Копировать ссылку",
|
||||
"Room owner": "Владелец комнаты",
|
||||
"(you)": "(вы)",
|
||||
"Remove {name}": "Удалить игрока {name}",
|
||||
"Remove": "Удалить",
|
||||
"Everyone plays the same five locations. The most points wins; equal totals share a place.": "Все играют на одинаковых пяти локациях. Побеждает игрок с наибольшим числом очков; при равном счёте место общее.",
|
||||
"Start multiplayer match": "Начать игру",
|
||||
"Leave room": "Покинуть комнату",
|
||||
"Close room": "Закрыть комнату",
|
||||
"Invite at least one friend before starting": "Перед началом пригласите хотя бы одного друга",
|
||||
"{count} points": "{count} очков",
|
||||
"Five-round totals": "Результаты пяти раундов",
|
||||
"Place": "Место",
|
||||
"Player": "Игрок",
|
||||
"Total points": "Всего очков",
|
||||
"Left room": "Покинул комнату",
|
||||
"Play this map again": "Сыграть на этой карте ещё раз",
|
||||
"Loading…": "Загрузка…",
|
||||
"Players": "Игроки",
|
||||
"Ready": "Готов",
|
||||
"Guess submitted": "Ответ отправлен",
|
||||
"Guessing": "Выбирает место",
|
||||
"Guess locked. Waiting for the other players.": "Ответ принят. Ждём остальных игроков.",
|
||||
"Time is up. Calculating round scores…": "Время вышло. Подсчитываем очки…",
|
||||
"Waiting for players to load the shared round…": "Ждём загрузки раунда у остальных игроков…",
|
||||
"Starting within {seconds}s": "Начало не позднее чем через {seconds} с",
|
||||
"Round standings": "Результаты раунда",
|
||||
"Next round in {seconds}s": "Следующий раунд через {seconds} с",
|
||||
"Copy the invite link from the field below": "Скопируйте ссылку из поля ниже",
|
||||
"Choose between 2 and 16 players": "Выберите от 2 до 16 игроков",
|
||||
"This name is already taken in this room": "Это имя уже занято в комнате",
|
||||
"Room owner permission required": "Это действие доступно владельцу комнаты",
|
||||
"Room membership permission required": "Сначала присоединитесь к комнате",
|
||||
"Room not found": "Комната не найдена",
|
||||
"Join this room to play": "Присоединитесь к комнате, чтобы играть",
|
||||
"Wait for the round to finish": "Дождитесь завершения раунда",
|
||||
"Players can only be removed before the match starts": "Удалять игроков можно только до начала игры"
|
||||
}
|
||||
|
||||
62
shared/multiplayer.ts
Normal file
62
shared/multiplayer.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { z } from "zod";
|
||||
import { displayNameSchema } from "./account";
|
||||
import type { Bounds, Difficulty } from "./protocol";
|
||||
|
||||
export const createRoomSchema = z.object({
|
||||
datasetId: z.string().uuid(),
|
||||
difficulty: z.enum(["easy", "normal", "hard"]),
|
||||
displayName: displayNameSchema,
|
||||
playerLimit: z.number().int().min(2).max(16),
|
||||
});
|
||||
export const joinRoomSchema = z.object({ displayName: displayNameSchema });
|
||||
export const roomRoundSchema = z.object({
|
||||
round: z.number().int().min(0).max(4),
|
||||
});
|
||||
export const roomGuessSchema = roomRoundSchema.extend({
|
||||
x: z.number().finite(),
|
||||
z: z.number().finite(),
|
||||
});
|
||||
export type RoomResult = {
|
||||
playerId: string;
|
||||
score: number;
|
||||
distance: number | null;
|
||||
timedOut: boolean;
|
||||
actual: { x: number; z: number };
|
||||
guess: { x: number; z: number } | null;
|
||||
};
|
||||
export type RoomView = {
|
||||
id: string;
|
||||
datasetId: string;
|
||||
name: string;
|
||||
difficulty: Difficulty;
|
||||
playerLimit: number;
|
||||
playerCount: number;
|
||||
status: "lobby" | "loading" | "playing" | "results" | "finished" | "closed";
|
||||
round: number;
|
||||
hostId: string;
|
||||
you: string | null;
|
||||
serverNow: number;
|
||||
transitionAt: string | null;
|
||||
players: {
|
||||
id: string;
|
||||
name: string;
|
||||
left: boolean;
|
||||
ready: boolean;
|
||||
submitted: boolean;
|
||||
total: number;
|
||||
rank: number;
|
||||
}[];
|
||||
rounds: {
|
||||
number: number;
|
||||
deadline: string | null;
|
||||
closedAt: string | null;
|
||||
results: RoomResult[];
|
||||
}[];
|
||||
myGuess: { x: number; z: number } | null;
|
||||
};
|
||||
export type RoomMedia = {
|
||||
round: number;
|
||||
map: string;
|
||||
bounds: Bounds;
|
||||
faces: string[];
|
||||
};
|
||||
Reference in New Issue
Block a user