commit a93b9c0ff9b184453a95cdde7edbe3700c5a4613
parent ba873087968796ad622f7968a39711ed974e7a5f
Author: Christos Margiolis <christos@FreeBSD.org>
Date: Sun, 9 Jun 2024 23:58:54 +0200
foobar
Diffstat:
11 files changed, 365 insertions(+), 154 deletions(-)
diff --git a/concurrent_programming/extern.h b/concurrent_programming/extern.h
@@ -0,0 +1,15 @@
+#ifndef _EXTERN_H_
+#define _EXTERN_H_
+
+#define CMD_LEN 32
+#define NAME_LEN 32
+#define MOVE_TIMEOUT 3
+#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
+
+enum {
+ MOVE_ROCK,
+ MOVE_PAPER,
+ MOVE_SCISSOR,
+};
+
+#endif /* _EXTERN_H_ */
diff --git a/concurrent_programming/rps_client.c b/concurrent_programming/rps_client.c
@@ -1,4 +1,3 @@
-#include <err.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -6,32 +5,34 @@
#include <netdb.h>
#include <netinet/in.h>
+#include <err.h>
+#include <errno.h>
#include <locale.h>
-#include <ncurses.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
+#include "extern.h"
struct command {
- char name[32];
- void (*func)(char *);
+ char name[CMD_LEN];
+ int (*func)(char *);
};
-static void cmd_challenge(char *);
-static void cmd_play(char *);
-static void cmd_msg(char *);
-static void cmd_list(char *);
-static void cmd_quit(char *);
-static void cmd_help(char *);
-static int init_curses(void);
+static int cmd_challenge(char *);
+static int cmd_play(char *);
+static int cmd_msg(char *);
+static int cmd_list(char *);
+static int cmd_quit(char *);
+static int cmd_help(char *);
static struct command *parse_command(char **);
+static void sighandler(int);
static void usage(void);
static struct command commands[] = {
- { "challenge", cmd_challenge }, /* /challenge <name|id> */
+ { "challenge", cmd_challenge }, /* /challenge <id> */
{ "play", cmd_play }, /* /play <rock|paper|scissor> */
{ "msg", cmd_msg }, /* /msg <message> */
{ "list", cmd_list }, /* /list */
@@ -44,38 +45,120 @@ static int xmax;
static int f_quit = 0;
static int fd;
-static void
+static int
cmd_challenge(char *args)
{
+ char cmd[CMD_LEN];
+ int id;
+
+ if (args == NULL) {
+ warnx("usage: /challenge <id>");
+ return (-1);
+ }
+
+ strlcpy(cmd, "challenge", sizeof(cmd));
+ if (send(fd, cmd, sizeof(cmd), 0) < 0) {
+ warn("send(challenge, cmd)");
+ return (-1);
+ }
+
+ id = strtol(args, NULL, 10);
+ if (errno == EINVAL || errno == ERANGE) {
+ warn("strtol(%s)", args);
+ return (-1);
+ }
+ if (send(fd, &id, sizeof(id), 0) < 0) {
+ warn("send(challenge, id)");
+ return (-1);
+ }
+
+ return (0);
}
-static void
+static int
cmd_play(char *args)
{
- /* TODO add fuzzy parsing */
- /* TODO ask for command and send it to server */
+ char cmd[CMD_LEN];
+ int move;
+
+ if (args == NULL) {
+ warnx("usage: /challenge <rock|paper|scissor>");
+ return (-1);
+ }
+
+ strlcpy(cmd, "play", sizeof(cmd));
+ if (send(fd, cmd, sizeof(cmd), 0) < 0) {
+ warn("send(play, cmd)");
+ return (-1);
+ }
+
+ if (strcmp(args, "rock") == 0)
+ move = MOVE_ROCK;
+ else if (strcmp(args, "paper") == 0)
+ move = MOVE_PAPER;
+ else if (strcmp(args, "scissor") == 0)
+ move = MOVE_SCISSOR;
+ else
+ warnx("invalid move: %s\n", args);
+
+ if (send(fd, &move, sizeof(move), 0) < 0) {
+ warn("send(play, move)");
+ return (-1);
+ }
+
+ return (0);
}
-static void
+static int
cmd_msg(char *args)
{
+ char cmd[CMD_LEN];
+
+ if (args == NULL) {
+ warnx("usage: /msg <message>");
+ return (-1);
+ }
+
+ strlcpy(cmd, "msg", sizeof(cmd));
+ if (send(fd, cmd, sizeof(cmd), 0) < 0) {
+ warn("send(msg)");
+ return (-1);
+ }
+
+ return (0);
}
-static void
+static int
cmd_list(char *args)
{
+ char cmd[CMD_LEN];
+
+ strlcpy(cmd, "list", sizeof(cmd));
+ if (send(fd, cmd, sizeof(cmd), 0) < 0) {
+ warn("send(list)");
+ return (-1);
+ }
+
+ return (0);
}
-/* TODO return int... */
-static void
+static int
cmd_quit(char *args)
{
+ char cmd[CMD_LEN];
+
f_quit = 1;
- if (send(fd, &f_quit, sizeof(f_quit), 0) < 0)
- ; /* TODO */
+
+ strlcpy(cmd, "quit", sizeof(cmd));
+ if (send(fd, cmd, sizeof(cmd), 0) < 0) {
+ warn("send(quit)");
+ return (-1);
+ }
+
+ return (0);
}
-static void
+static int
cmd_help(char *args)
{
printf("Available commands:\n");
@@ -84,35 +167,6 @@ cmd_help(char *args)
printf("/msg <message>\t\t\tSend a message to the global chat\n");
printf("/quit\t\t\t\tQuit the game\n");
printf("/help\t\t\t\tShow this help message\n");
-}
-
-static int
-init_curses(void)
-{
- if (!initscr())
- return (-1);
-
- /* Don't echo keypresses. */
- noecho();
- /* Disable line buffering. */
- cbreak();
- /* Hide the cursor. */
- curs_set(false);
- /* Allow arrow-key usage. */
- keypad(stdscr, true);
- /* ESC has a small delay after it's pressed. Remove it. */
- set_escdelay(0);
- /*
- * Don't wait for a keypress -- just continue if there's no keypress
- * within 1000 milliseconds. We could set the delay to 0 milliseconds,
- * but this will most likely burn the CPU.
- */
- timeout(1000);
- (void)getmaxyx(stdscr, ymax, xmax);
- start_color();
- /* Use the terminal's colorscheme. */
- use_default_colors();
- /* TODO init pairs */
return (0);
}
@@ -156,6 +210,9 @@ parse_command(char **args)
while (**args == ' ')
(*args)++;
}
+ /* FIXME */
+ /*if (**args == '\0')*/
+ /**args = NULL;*/
cmd = strtok(line, " ");
if (cmd == NULL) {
@@ -178,6 +235,12 @@ parse_command(char **args)
}
static void
+sighandler(int sig)
+{
+ f_quit = 1;
+}
+
+static void
usage(void)
{
fprintf(stderr, "usage: %1$s [-p port] <hostname|ipv4_addr>\n",
@@ -190,8 +253,10 @@ main(int argc, char *argv[])
{
struct sockaddr_in sin;
struct hostent *hp;
+ struct sigaction sa;
struct command *cmd;
- char *args;
+ char *args, *s;
+ char nick[NAME_LEN];
int port = 9999;
int ch;
@@ -211,6 +276,40 @@ main(int argc, char *argv[])
if (argc < 1)
usage();
+ /*
+ * Handle termination signals so we don't exit abnormally (i.e without
+ * cleaning up resources).
+ */
+ memset(&sa, 0, sizeof(sa));
+ sigfillset(&sa.sa_mask);
+ sa.sa_handler = sighandler;
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ err(1, "sigaction(SIGINT)");
+ if (sigaction(SIGTERM, &sa, NULL) < 0)
+ err(1, "sigaction(SIGTERM)");
+
+ if (!setlocale(LC_ALL, ""))
+ err(1, "setlocale");
+
+ /* Choose nickname. */
+ printf("\rEnter nickname (no whitespaces): ");
+ fgets(nick, sizeof(nick), stdin);
+ s = nick;
+
+ if (*s == '\n' || *s == '\0')
+ errx(1, "empty nickname");
+
+ /* Get rid of leading whitespaces. */
+ while (*s == ' ')
+ s++;
+
+ /* Get rid of trailing newlines and whitespaces. */
+ for (; *s != '\0'; s++) {
+ if (*s != '\n' && *s != ' ')
+ continue;
+ *s = '\0';
+ break;
+ }
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
err(1, "socket(AF_INET)");
@@ -225,13 +324,11 @@ main(int argc, char *argv[])
if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
err(1, "connect");
- /* TODO nickname selection (server keeps asking until unique) */
-
- if (!setlocale(LC_ALL, ""))
- err(1, "setlocale");
+ /* Send nickname to server */
+ if (send(fd, nick, sizeof(nick), 0) < 0)
+ err(1, "send(nick)");
- /*if (init_curses() < 0)*/
- /*errx(1, "could not initialize ncurses");*/
+ cmd_list(NULL);
for (;;) {
if (f_quit)
@@ -239,14 +336,12 @@ main(int argc, char *argv[])
/* TODO redraw */
if ((cmd = parse_command(&args)) == NULL)
continue;
- /* TODO connect to server */
- cmd->func(args);
+ if (cmd->func(args) < 0)
+ warnx("/%s(%s) failed\n", cmd->name, args);
if (args != NULL)
free(args);
}
close(fd);
- /*(void)endwin();*/
-
return (0);
}
diff --git a/concurrent_programming/rps_server.c b/concurrent_programming/rps_server.c
@@ -1,5 +1,8 @@
+#include <sys/mman.h>
+#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/types.h>
+#include <sys/wait.h>
#include <arpa/inet.h>
#include <netdb.h>
@@ -12,27 +15,161 @@
#include <string.h>
#include <unistd.h>
-static int srv(int);
+#include "extern.h"
+
+struct user {
+ int fd;
+ int id;
+ char name[NAME_LEN];
+ int move;
+ int nplays;
+ int nwins;
+ struct user *opponent;
+ TAILQ_ENTRY(user) next;
+};
+
+static int srv(struct user *);
static void sighandler(int);
static void usage(void);
static volatile sig_atomic_t f_quit = 0;
+static int ids = 0;
+static int *nusers;
+static TAILQ_HEAD(, user) users = TAILQ_HEAD_INITIALIZER(users);
+
+static void
+remove_user(struct user *user)
+{
+ struct user *up;
+
+ while (!TAILQ_EMPTY(&users)) {
+ up = TAILQ_FIRST(&users);
+ TAILQ_REMOVE(&users, up, next);
+ close(up->fd);
+ munmap(up, sizeof(struct user));
+ break;
+ }
+}
static int
-srv(int fd)
+srv(struct user *user)
{
- int quit;
+ struct user *up;
+ char cmd[CMD_LEN];
+ int rc = 0, tmp, quit, id;
for (;;) {
- if (recv(fd, &quit, sizeof(quit), 0) < 0)
+ tmp = recv(user->fd, cmd, sizeof(cmd), 0);
+ if (tmp < 0) {
+ warn("recv(%d, cmd)", user->fd);
+ rc = -1;
break;
- if (quit) {
- printf("%s(): quit triggered\n", __func__);
+ } else if (tmp == 0)
break;
+
+ if (strcmp(cmd, "challenge") == 0) {
+ if (recv(user->fd, &id, sizeof(id), 0) < 0) {
+ warn("recv(%d, id)", user->fd);
+ rc = -1;
+ break;
+ }
+ up = NULL;
+ TAILQ_FOREACH(up, &users, next) {
+ if (up->id == id)
+ break;
+ }
+ if (up == NULL) {
+ warnx("id=%d not found", id);
+ continue;
+ }
+ if (up->opponent != NULL) {
+ warnx("%s is already challenged", up->name);
+ continue;
+ }
+ user->opponent = up;
+ up->opponent = user;
+ printf("%s is challenging %d\n", user->name, id);
+ /* TODO challenge available user */
+ } else if (strcmp(cmd, "play") == 0) {
+ if (recv(user->fd, &user->move,
+ sizeof(user->move), 0) < 0) {
+ warn("recv(%d, play)", user->fd);
+ rc = -1;
+ break;
+ }
+
+ /* Cannot play when unchallenged. */
+ if (user->opponent == NULL)
+ continue;
+
+ /* Give the opponent 3 seconds to make a move. */
+ for (int i = 0; user->opponent->move < 0 &&
+ i < MOVE_TIMEOUT; i++) {
+ printf("[%s - %s] %s sleeping\n",
+ user->name, user->opponent->name, user->name);
+ sleep(1);
+ }
+
+ /* Didn't respond, he lost. */
+ if (user->opponent->move < 0) {
+ printf("[%s - %s] opponent didn't respond\n",
+ user->name, user->opponent->name);
+ user->move = -1;
+ user->opponent = NULL;
+ /*user->opponent->opponent = NULL;*/
+ user->nplays++;
+ user->nwins++;
+ }
+
+ printf("%s=%d, %s=%d\n", user->name, user->move,
+ user->opponent->name, user->opponent->move);
+
+ switch (user->move) {
+ case MOVE_ROCK:
+ if (user->opponent->move == MOVE_SCISSOR)
+ user->nwins++;
+ break;
+ case MOVE_PAPER:
+ if (user->opponent->move == MOVE_ROCK)
+ user->nwins++;
+ break;
+ case MOVE_SCISSOR:
+ if (user->opponent->move == MOVE_PAPER)
+ user->nwins++;
+ break;
+ default:
+ warnx("invalid move: %d", user->move);
+ continue;
+ }
+ printf("[%s - %s] = [%d - %d]\n",
+ user->name, user->opponent->name,
+ user->nwins, user->opponent->nwins);
+
+ user->move = -1;
+ user->opponent = NULL;
+ user->opponent->opponent = NULL;
+ user->nplays++;
+ } else if (strcmp(cmd, "msg") == 0) {
+ /* TODO post message to global chat (list) */
+ } else if (strcmp(cmd, "list") == 0) {
+ printf("ID\tNAME\tPLAYED\tWON\n");
+ TAILQ_FOREACH(up, &users, next) {
+ printf("%d\t%s\t%d\t%d\n",
+ up->id, up->name, up->nplays, up->nwins);
+ }
+ /* TODO send as nvlist */
+ } else if (strcmp(cmd, "quit") == 0) {
+ break;
+ } else {
+ warnx("received unknown command: %s", cmd);
}
}
- return (0);
+ printf("%s: client disconnected: %s#%d (fd=%d)\n",
+ getprogname(), user->name, user->id, user->fd);
+ remove_user(user);
+
+ return (rc);
}
static void
@@ -55,10 +192,10 @@ main(int argc, char *argv[])
struct sockaddr_in sin;
struct hostent *hp;
struct sigaction sa;
+ struct user *user;
int backlog = 10;
int port = 9999;
int sfd;
- int cfd;
int ch;
while ((ch = getopt(argc, argv, "b:p:")) != -1) {
@@ -136,13 +273,52 @@ main(int argc, char *argv[])
if (listen(sfd, backlog) < 0)
err(1, "listen");
+ /* mmap(2) this that we can modify decrement it from the child. */
+ nusers = mmap(NULL, sizeof(*nusers), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (nusers == MAP_FAILED)
+ err(1, "mmap");
+ *nusers = 0;
+
+ TAILQ_INIT(&users);
+
for (;;) {
/* We caught a termination signal. */
if (f_quit)
break;
- if ((cfd = accept(sfd, NULL, NULL)) < 0)
+
+ /*
+ * Allocate user structure using mmap(2) so that we can modify
+ * the userlist from the child process.
+ */
+ user = mmap(NULL, sizeof(struct user), PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (user == MAP_FAILED)
+ err(1, "mmap");
+
+ if ((user->fd = accept(sfd, NULL, NULL)) < 0) {
+ warn("accept(%d)", user->fd);
+ remove_user(user);
+ continue;
+ }
+ /* Receive nickname and assign ID to user. */
+ if (recv(user->fd, user->name, sizeof(user->name), 0) < 0) {
+ warn("recv(%d, nick)", user->fd);
+ remove_user(user);
continue;
- printf("[%s] client connected: %d\n", getprogname(), cfd);
+ }
+
+ user->id = ++ids;
+ user->nplays = 0;
+ user->nwins = 0;
+ user->opponent = NULL;
+ TAILQ_INSERT_TAIL(&users, user, next);
+
+ (*nusers)++;
+
+ printf("%s: active users=%d\n", getprogname(), *nusers);
+ printf("%s: client connected: %s#%d (fd=%d)\n",
+ getprogname(), user->name, user->id, user->fd);
/*
* Create a child process to serve the client so the parent can
* continue waiting for another client to serve.
@@ -151,19 +327,18 @@ main(int argc, char *argv[])
case -1:
err(1, "fork");
case 0:
- if (srv(cfd) < 0)
- warnx("srv failed");
- printf("[%s] client disconnected: %d\n",
- getprogname(), cfd);
+ if (srv(user) < 0)
+ warnx("srv(%s#%d) failed", user->name, user->id);
+ (*nusers)--;
_exit(0);
default:
- close(cfd);
+ close(user->fd);
}
}
/* Will get here only if a termination signal is caught. */
close(sfd);
- close(cfd);
+ remove_user(user);
return (0);
}
diff --git a/java_multimedia/VideoStreaming/.classpath b/java_multimedia/VideoStreaming/.classpath
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="con" path="org.eclipse.jdt.USER_LIBRARY/JavaFX">
- <attributes>
- <attribute name="module" value="true"/>
- </attributes>
- </classpathentry>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
- <attributes>
- <attribute name="module" value="true"/>
- </attributes>
- </classpathentry>
- <classpathentry kind="output" path="bin"/>
-</classpath>
diff --git a/java_multimedia/VideoStreaming/.project b/java_multimedia/VideoStreaming/.project
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>VideoStreaming</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/java_multimedia/VideoStreaming/.settings/org.eclipse.core.resources.prefs b/java_multimedia/VideoStreaming/.settings/org.eclipse.core.resources.prefs
@@ -1,2 +0,0 @@
-eclipse.preferences.version=1
-encoding/<project>=UTF-8
diff --git a/java_multimedia/VideoStreaming/.settings/org.eclipse.jdt.core.prefs b/java_multimedia/VideoStreaming/.settings/org.eclipse.jdt.core.prefs
@@ -1,14 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
-org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
-org.eclipse.jdt.core.compiler.compliance=11
-org.eclipse.jdt.core.compiler.debug.lineNumber=generate
-org.eclipse.jdt.core.compiler.debug.localVariable=generate
-org.eclipse.jdt.core.compiler.debug.sourceFile=generate
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
-org.eclipse.jdt.core.compiler.release=enabled
-org.eclipse.jdt.core.compiler.source=11
diff --git a/java_multimedia/VideoStreaming/bin/module-info.class b/java_multimedia/VideoStreaming/bin/module-info.class
Binary files differ.
diff --git a/java_multimedia/VideoStreaming/bin/video_streaming/Main.class b/java_multimedia/VideoStreaming/bin/video_streaming/Main.class
Binary files differ.
diff --git a/java_multimedia/VideoStreaming/src/module-info.java b/java_multimedia/VideoStreaming/src/module-info.java
@@ -1,6 +0,0 @@
-module video_streaming {
- exports video_streaming;
- requires transitive javafx.controls;
- requires javafx.fxml;
- requires javafx.graphics;
-}
-\ No newline at end of file
diff --git a/java_multimedia/VideoStreaming/src/video_streaming/Main.java b/java_multimedia/VideoStreaming/src/video_streaming/Main.java
@@ -1,19 +0,0 @@
-package video_streaming;
-
-import javafx.application.Application;
-import javafx.scene.Scene;
-import javafx.scene.layout.StackPane;
-import javafx.stage.Stage;
-
-public class Main extends Application {
- @Override
- public void start(Stage stg) throws Exception {
- stg.setTitle("Video Streaming Client");
- stg.setScene(new Scene(new StackPane(), 350, 230));
- stg.show();
- }
-
- public static void main(String[] args) {
- launch(args);
- }
-}