Skip to content

Commit

Permalink
Allow re-joining sandbox games and specifying adjacency type
Browse files Browse the repository at this point in the history
  • Loading branch information
Oliveriver committed Aug 6, 2024
1 parent f3af3f5 commit 8cb93c7
Show file tree
Hide file tree
Showing 17 changed files with 4,502 additions and 27 deletions.
10 changes: 7 additions & 3 deletions client/src/components/context/GameContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ export const GameContextProvider = ({ children }: PropsWithChildren) => {
const contextValue = useMemo(
() => ({
game: created ?? joined,
createGame: async (isSandbox: boolean, player: Nation | null) =>
createGame({ isSandbox, player }),
joinGame: async (gameId: number, player: Nation | null) => joinGame({ gameId, player }),
createGame: async (
isSandbox: boolean,
player: Nation | null,
hasStrictAdjacencies: boolean,
) => createGame({ isSandbox, player, hasStrictAdjacencies }),
joinGame: async (gameId: number, isSandbox: boolean, player: Nation | null) =>
joinGame({ gameId, isSandbox, player }),
exitGame: () => {
queryClient.removeQueries();
resetCreate();
Expand Down
20 changes: 18 additions & 2 deletions client/src/components/pages/JoinGamePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import GameContext from '../context/GameContext';
import Error from '../user-interface/common/Error';
import TextInput from '../user-interface/common/TextInput';
import { isInteger } from '../../utils/numberUtils';
import Select from '../user-interface/common/Select';

type JoinGameProps = {
setViewOption: (option: SetupViewOption) => void;
Expand All @@ -16,6 +17,7 @@ const JoinGamePage = ({ setViewOption }: JoinGameProps) => {
const { joinGame, exitGame, isLoading, error } = useContext(GameContext);

const [gameId, setGameId] = useState<number>();
const [isSandbox, setIsSandbox] = useState(false);
const [player, setPlayer] = useState<Nation>();

const onGameIdChanged = (value: string) => {
Expand All @@ -30,7 +32,7 @@ const JoinGamePage = ({ setViewOption }: JoinGameProps) => {

const onJoinPressed = () => {
if (gameId === undefined) return;
joinGame(gameId ?? 0, player ?? null);
joinGame(gameId ?? 0, isSandbox, player ?? null);
};

const onBackPressed = () => {
Expand All @@ -42,7 +44,21 @@ const JoinGamePage = ({ setViewOption }: JoinGameProps) => {
<div className="flex flex-col w-screen h-screen items-center gap-4 pt-24">
<img alt="Logo" src="./logo.png" className="w-64 pb-8" />
<TextInput placeholder="Game ID" onChange={onGameIdChanged} />
<NationSelect selectedNation={player} setSelectedNation={setPlayer} />
<Select
options={[
{
value: 'false',
text: 'Normal',
},
{
value: 'true',
text: 'Sandbox',
},
]}
setValue={(value) => setIsSandbox(value === 'true')}
selectedValue={isSandbox ? 'true' : 'false'}
/>
{!isSandbox && <NationSelect selectedNation={player} setSelectedNation={setPlayer} />}
<Button
text="Join"
minWidth={256}
Expand Down
17 changes: 16 additions & 1 deletion client/src/components/pages/NewGamePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ const NewGamePage = ({ setViewOption }: NewGamePageProps) => {

const [isSandbox, setIsSandbox] = useState(false);
const [player, setPlayer] = useState<Nation>();
const [hasStrictAdjacencies, setHasStrictAdjacencies] = useState(true);

const onCreatePressed = () => createGame(isSandbox, player ?? null);
const onCreatePressed = () => createGame(isSandbox, player ?? null, hasStrictAdjacencies);

const onBackPressed = () => {
exitGame();
Expand All @@ -42,6 +43,20 @@ const NewGamePage = ({ setViewOption }: NewGamePageProps) => {
selectedValue={isSandbox ? 'true' : 'false'}
/>
{!isSandbox && <NationSelect selectedNation={player} setSelectedNation={setPlayer} />}
<Select
options={[
{
value: 'true',
text: 'Strict adjacencies',
},
{
value: 'false',
text: 'Loose adjacencies',
},
]}
setValue={(value) => setHasStrictAdjacencies(value === 'true')}
selectedValue={hasStrictAdjacencies ? 'true' : 'false'}
/>
<Button text="Create" minWidth={256} onClick={onCreatePressed} isBusy={isLoading} />
{error && <Error error={error} />}
<div className="absolute bottom-10">
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/user-interface/GameDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const GameDetails = () => {
style={{ backgroundColor: colours.uiOverlay }}
>
<div className="flex flex-row items-center text-sm">
<p>Game ID:</p>
<p title={JSON.stringify(game)}>Game ID:</p>
<p className="ml-3 mr-1">{id}</p>
<button
type="button"
Expand Down
8 changes: 7 additions & 1 deletion client/src/hooks/api/useCreateGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ import routes from '../../api/routes';
type CreationRequest = {
isSandbox: boolean;
player: Nation | null;
hasStrictAdjacencies: boolean;
};

const createGame = async ({ isSandbox, player }: CreationRequest): Promise<Game> => {
const createGame = async ({
isSandbox,
player,
hasStrictAdjacencies,
}: CreationRequest): Promise<Game> => {
const route = routes.createGame();
const response = await axios.post<Game>(route, {
isSandbox,
player,
hasStrictAdjacencies,
});
return response.data;
};
Expand Down
4 changes: 3 additions & 1 deletion client/src/hooks/api/useJoinGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import routes from '../../api/routes';

type JoinRequest = {
gameId: number;
isSandbox: boolean;
player: Nation | null;
};

const joinGame = async ({ gameId, player }: JoinRequest): Promise<Game> => {
const joinGame = async ({ gameId, isSandbox, player }: JoinRequest): Promise<Game> => {
const route = routes.joinGame(gameId);
const response = await axios.put<Game>(route, {
isSandbox,
player,
});
return response.data;
Expand Down
8 changes: 6 additions & 2 deletions client/src/types/context/gameState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ import Game from '../game';

type GameState = {
game: Game | null;
createGame: (isSandbox: boolean, player: Nation | null) => Promise<void>;
joinGame: (gameId: number, player: Nation | null) => Promise<void>;
createGame: (
isSandbox: boolean,
player: Nation | null,
hasStrictAdjacencies: boolean,
) => Promise<void>;
joinGame: (gameId: number, isSandbox: boolean, player: Nation | null) => Promise<void>;
exitGame: () => void;
isLoading: boolean;
error: Error | null;
Expand Down
1 change: 1 addition & 0 deletions client/src/types/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Nation from './enums/nation';

type Game = {
id: number;
hasStrictAdjacencies: boolean;
player: Nation | null;
};

Expand Down
27 changes: 18 additions & 9 deletions server/Controllers/GameController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public async Task<ActionResult<Game>> CreateGame([FromBody] GameCreationRequest
{
var isSandbox = request.IsSandbox;
var player = request.Player;
var hasStrictAdjacencies = request.HasStrictAdjacencies;

if (isSandbox && player != null)
{
Expand All @@ -34,13 +35,13 @@ public async Task<ActionResult<Game>> CreateGame([FromBody] GameCreationRequest

if (isSandbox)
{
var game = await gameRepository.CreateSandboxGame();
return Ok(new Game(game.Id));
var game = await gameRepository.CreateSandboxGame(hasStrictAdjacencies);
return Ok(new Game(game.Id, game.HasStrictAdjacencies));
}
else
{
var (game, chosenPlayer) = await gameRepository.CreateNormalGame(player);
return Ok(new Game(game.Id, chosenPlayer));
var (game, chosenPlayer) = await gameRepository.CreateNormalGame(player, hasStrictAdjacencies);
return Ok(new Game(game.Id, game.HasStrictAdjacencies, chosenPlayer));
}
}

Expand All @@ -49,22 +50,30 @@ public async Task<ActionResult<Game>> CreateGame([FromBody] GameCreationRequest
public async Task<ActionResult<Game>> JoinGame([FromRoute] int gameId, [FromBody] GameJoinRequest request)
{
var player = request.Player;
logger.LogInformation("Requesting to join game {GameId} as {Player}", gameId, player);
var isSandbox = request.IsSandbox;

try
{
var (game, chosenPlayer) = await gameRepository.JoinGame(gameId, player);
return Ok(new Game(game.Id, chosenPlayer));
if (isSandbox)
{
var game = await gameRepository.JoinSandboxGame(gameId);
return Ok(new Game(game.Id, game.HasStrictAdjacencies));
}
else
{
var (game, chosenPlayer) = await gameRepository.JoinNormalGame(gameId, player);
return Ok(new Game(game.Id, game.HasStrictAdjacencies, chosenPlayer));
}
}
catch (KeyNotFoundException)
{
logger.LogError("Attempted to join non-existent game {GameId}", gameId);
return NotFound($"No game with ID {gameId} found");
}
catch (InvalidOperationException)
catch (InvalidOperationException error)
{
logger.LogWarning("Failed to join game {GameId}", gameId);
return BadRequest("Unable to join in-progress game as random nation");
return BadRequest(error.Message);
}
}

Expand Down
3 changes: 3 additions & 0 deletions server/Entities/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ public class Game
{
public int Id { get; set; }

public bool IsSandbox { get; set; }
public bool HasStrictAdjacencies { get; set; }

public List<Nation> Players { get; set; } = null!;
public virtual World? World { get; set; }
public List<Nation> PlayersSubmitted { get; set; } = null!;
Expand Down
Loading

0 comments on commit 8cb93c7

Please sign in to comment.