From 79859bbec78d83d3e1d2dcefaa1b4c4e603b3268 Mon Sep 17 00:00:00 2001 From: zongor Date: Sun, 10 Sep 2023 18:51:11 -0400 Subject: [PATCH] add server, incredibly annoying hex hack --- common/sql/test.db3 | Bin 12288 -> 8192 bytes fortran/client/main.f90 | 36 ++++--- fortran/server/README.md | 24 +++++ fortran/server/app/main.f90 | 37 +++++++ fortran/server/fpm.toml | 19 ++++ fortran/server/src/db.f90 | 196 ++++++++++++++++++++++++++++++++++++ fortran/server/test.sh | 2 + fortran/www/README.md | 3 +- fortran/www/app/main.f90 | 31 +++++- fortran/www/fpm.toml | 2 +- fortran/www/src/db.f90 | 34 ++++--- 11 files changed, 351 insertions(+), 33 deletions(-) create mode 100644 fortran/server/README.md create mode 100644 fortran/server/app/main.f90 create mode 100644 fortran/server/fpm.toml create mode 100644 fortran/server/src/db.f90 create mode 100755 fortran/server/test.sh diff --git a/common/sql/test.db3 b/common/sql/test.db3 index 9f135d683e65a72332bf27b606e1a726812b3127..d695b7dcab82c6309ca6735e57a430a8a1ece272 100644 GIT binary patch literal 8192 zcmeI#ze~eF6bJCTP%t1;w@k_d9aMrKjxNS>XfcYW8SE6U%{2zwB-}-+qxg^bclA$j z^B)kCSgnK5wZr#uKOc|d@!76->!+onQLZPlq+_d@-SUno0O|w{? zKzN~`&^z-5H7VLlW9nXq;#>rD6Lhbx6%FYzxj-{XDi5$ zmLLED2tWV=5P$##AOHafKmY;|SR$~?*O{}!Ez7p;*6?0F=%WK^;!!=l0efTb@#ls8 E1;8F!;s5{u delta 409 zcmZp0Xh@hKEy%&Zz`zW}j6j-WqK+|8P%rHjFObK`ugSo#$)~rm@Bl9p<oy5S`M#0GN!jkjp# diff --git a/fortran/client/main.f90 b/fortran/client/main.f90 index 06f1018..406d181 100644 --- a/fortran/client/main.f90 +++ b/fortran/client/main.f90 @@ -10,6 +10,7 @@ program main type(vector3) :: npc_position type(vector3) :: player_position real(c_float) :: npc_dir + logical :: player_updated ! ! implement a login and logout screen @@ -31,6 +32,10 @@ program main call set_target_fps(60_c_int) + ! + ! do login stuff here + ! + !Main game loop do while (.not. window_should_close()) ! Detect window close button or ESC key @@ -51,17 +56,19 @@ program main camera%target%y = player_position%y camera%target%z = player_position%z - if (npc_position%x < -10) then - npc_dir = 0.1_c_float - else if (npc_position%x > 10) then - npc_dir = -0.1_c_float - end if - npc_position%x = npc_position%x + npc_dir - ! - ! implement a user input for texting - ! https://www.raylib.com/examples/text/loader.html?name=text_input_box - ! + ! if player updated + ! send new player postition to server + ! else + ! send ping to server + ! read response + ! for each user in logged in + ! if (npc_position%x < -10) then + ! npc_dir = 0.1_c_float + ! else if (npc_position%x > 10) then + ! npc_dir = -0.1_c_float + ! end if + ! npc_position%x = npc_position%x + npc_dir call begin_drawing() call clear_background(RAYWHITE) @@ -70,10 +77,7 @@ program main ! Draw floor call draw_grid(30_c_int, 1.0_c_float) - ! Draw user & others text over them - ! https://www.raylib.com/examples/text/loader.html?name=text_draw_3d - - !Draw npc cube + ! Draw other users call draw_cube(npc_position, 0.5_c_float, 0.5_c_float, 0.5_c_float, GRAY) call draw_cube_wires(npc_position, 0.5_c_float, 0.5_c_float, 0.5_c_float, DARKGRAY) @@ -85,6 +89,10 @@ program main end do + ! + ! send logout call to server + ! + call close_window() !Close window and OpenGL context contains diff --git a/fortran/server/README.md b/fortran/server/README.md new file mode 100644 index 0000000..b2e584a --- /dev/null +++ b/fortran/server/README.md @@ -0,0 +1,24 @@ +# server + +- request + + - int :: request_type + - ping + - login + - logout + - move + - str(24) :: username + - int :: timestamp + - str(?) :: data + - int :: data_length + - double :: x_pos + - double :: y_pos + +- response + - int :: response_type + - int :: number_of_records + - array :: records + - str(24) :: username + - str(24) :: color + - double :: x_pos + - double :: y_pos diff --git a/fortran/server/app/main.f90 b/fortran/server/app/main.f90 new file mode 100644 index 0000000..f23f22b --- /dev/null +++ b/fortran/server/app/main.f90 @@ -0,0 +1,37 @@ +program main + use, intrinsic :: iso_c_binding + use iso_fortran_env + use :: sqlite3 + use db + implicit none + + + character, dimension(:), allocatable :: form_data + character(len=128):: db_path + integer :: err, i, length + logical :: exist + + inquire (file="debug.log", exist=exist) + if (exist) then + open (12, file="debug.log", status="old", position="append", action="write") + else + open (12, file="debug.log", status="new", action="write") + end if + + ! do while not logged out + + ! read message from stdin + + ! if ping then get all logged in users and return their positions to client + + ! if move update new pos to database + + ! if logout update logged_in to database + + ! end do + + ! send logout all to clients + +contains + +end program main diff --git a/fortran/server/fpm.toml b/fortran/server/fpm.toml new file mode 100644 index 0000000..9d2c8bb --- /dev/null +++ b/fortran/server/fpm.toml @@ -0,0 +1,19 @@ +name = "fortran-mmo-server" +version = "0.1.0" +license = "MIT" +author = "zongor" +maintainer = "admin@alfrescocavern.com" +copyright = "Copyright 2023, zongor" +[build] +auto-executables = true +auto-tests = true +auto-examples = true +module-naming = false +[install] +library = false +[fortran] +implicit-typing = false +implicit-external = false +source-form = "free" +[dependencies] +fortran-sqlite3 = { git = "https://github.com/interkosmos/fortran-sqlite3.git" } \ No newline at end of file diff --git a/fortran/server/src/db.f90 b/fortran/server/src/db.f90 new file mode 100644 index 0000000..5cac73e --- /dev/null +++ b/fortran/server/src/db.f90 @@ -0,0 +1,196 @@ +! db.f90 +module db + !! Database abstraction layer. + use, intrinsic :: iso_c_binding + use :: sqlite3 + implicit none + private + + integer, parameter, public :: DB_OK = SQLITE_OK + + type, public :: db_type + type(c_ptr) :: ptr = c_null_ptr + end type db_type + + public :: db_close + public :: db_open + public :: db_create_users + public :: db_get_logged_in_users + public :: db_add_user + public :: db_delete_user + + private :: db_error + private :: db_exec +contains + + + integer function db_open(db, path) result(rc) + !! Opens database. + type(db_type), intent(inout) :: db + character(len=*), intent(in) :: path + + rc = sqlite3_open(path, db%ptr) + call db_error(rc, 'sqlite3_open()') + end function db_open + + integer function db_exec(db, query) result(rc) + !! Executes SQLite query. + type(db_type), intent(inout) :: db + character(len=*), intent(in) :: query + character(len=:), allocatable :: err_msg + + rc = sqlite3_exec(db%ptr, query, c_null_ptr, c_null_ptr, err_msg) + call db_error(rc, 'sqlite3_exec()', err_msg) + end function db_exec + + integer function db_close(db) result(rc) + !! Closes database. + type(db_type), intent(inout) :: db + + rc = sqlite3_close(db%ptr) + call db_error(rc, 'sqlite3_close()') + end function db_close + + integer function db_create_users(db) result(rc) + !! Creates database tables. + type(db_type), intent(inout) :: db + + ! Create table "users". + rc = db_exec(db, "CREATE TABLE users " & + // "(id INTEGER PRIMARY KEY ASC, " & + // "username TEXT, password TEXT, " & + // "apperance_r INTEGER, apperance_g INTEGER, apperance_b INTEGER, " & + // "x_pos REAL, y_pos REAL, logged_in INTEGER, " & + // "created INTEGER);") + if (rc /= SQLITE_OK) return + end function db_create_users + + integer function db_add_user(db, username, password, apperance_r, apperance_g, apperance_b, & + x_pos, y_pos, logged_in, created) result(rc) + + !! Adds student to database. + type(db_type), intent(inout) :: db + character(len=*), intent(in) :: username + character(len=*), intent(in) :: password + integer(kind=c_int), intent(in) :: apperance_r + integer(kind=c_int), intent(in) :: apperance_g + integer(kind=c_int), intent(in) :: apperance_b + real(kind=c_double), intent(in) :: x_pos + real(kind=c_double), intent(in) :: y_pos + integer(kind=c_int), intent(in) :: logged_in + integer(kind=c_int), intent(in) :: created + type(c_ptr) :: stmt + + ! Insert values through prepared statement. + rc = sqlite3_prepare_v2(db%ptr, "INSERT INTO users(username, password, " & + //"apperance_r, apperance_g, apperance_b, x_pos, y_pos, logged_in, created) VALUES (?,?,?,?,?,?,?,?,?)", stmt) + call db_error(rc, 'sqlite3_prepare_v2()') + + rc = sqlite3_bind_text(stmt, 1, username) + call db_error(rc, 'sqlite3_bind_text()') + rc = sqlite3_bind_text(stmt, 2, password) + call db_error(rc, 'sqlite3_bind_text()') + rc = sqlite3_bind_int(stmt, 3, apperance_r) + call db_error(rc, 'sqlite3_bind_int()') + rc = sqlite3_bind_int(stmt, 4, apperance_g) + call db_error(rc, 'sqlite3_bind_int()') + rc = sqlite3_bind_int(stmt, 5, apperance_b) + call db_error(rc, 'sqlite3_bind_int()') + rc = sqlite3_bind_double(stmt, 6, x_pos) + call db_error(rc, 'sqlite3_bind_double()') + rc = sqlite3_bind_double(stmt, 7, y_pos) + call db_error(rc, 'sqlite3_bind_double()') + rc = sqlite3_bind_int(stmt, 8, logged_in) + call db_error(rc, 'sqlite3_bind_int()') + rc = sqlite3_bind_int(stmt, 9, created) + call db_error(rc, 'sqlite3_bind_int()') + + ! Insert bound value into database. + rc = sqlite3_step(stmt) + call db_error(rc, 'sqlite3_step()') + + ! Clean-up prepared statement. + rc = sqlite3_finalize(stmt) + call db_error(rc, 'sqlite3_finalize()') + end function db_add_user + + + integer function db_delete_user(db, username) result(rc) + !! Adds student to database. + type(db_type), intent(inout) :: db + character(len=*), intent(in) :: username + type(c_ptr) :: stmt + + ! Insert values through prepared statement. + rc = sqlite3_prepare_v2(db%ptr, "DELETE FROM users WHERE users.username = ?;", stmt) + call db_error(rc, 'sqlite3_prepare_v2()') + + rc = sqlite3_bind_text(stmt, 1, username) + call db_error(rc, 'sqlite3_bind_text()') + + ! Insert bound value into database. + rc = sqlite3_step(stmt) + call db_error(rc, 'sqlite3_step()') + + ! Clean-up prepared statement. + rc = sqlite3_finalize(stmt) + call db_error(rc, 'sqlite3_finalize()') + end function db_delete_user + + integer function db_get_logged_in_users(db) result(rc) + !! Prints number of courses per student to standard output. + type(db_type), intent(inout) :: db + + type(c_ptr) :: stmt + character(len=24) :: username + real(kind=c_double) :: x_pos, y_pos + + rc = sqlite3_prepare_v2(db%ptr, & + "SELECT username, x_pos, y_pos " // & + "FROM users u WHERE u.logged_in = 1;", stmt) + call db_error(rc, 'sqlite3_prepare_v2()') + + step_loop: do + rc = sqlite3_step(stmt) + + select case (rc) + case (SQLITE_ROW) + username = sqlite3_column_text(stmt, 0) + x_pos = sqlite3_column_double(stmt, 1) + y_pos = sqlite3_column_double(stmt, 1) + write(12, *) username, x_pos, y_pos + + case (SQLITE_DONE) + exit step_loop + + case default + call db_error(rc, 'sqlite3_step()') + exit step_loop + end select + end do step_loop + + rc = sqlite3_finalize(stmt) + call db_error(rc, 'sqlite3_finalize()') + end function db_get_logged_in_users + + subroutine db_error(code, proc, err_msg) + !! Prints error message. + integer, intent(in) :: code + character(len=*), intent(in), optional :: proc + character(len=*), intent(in), optional :: err_msg + + if (code == SQLITE_OK .or. code == SQLITE_DONE) return + + if (present(proc) .and. present(err_msg)) then + write(12, '(a, ": ", a, " (", i0, ")")') proc, err_msg, code + return + end if + + if (present(proc)) then + write(12, '(a, ": ", i0)') proc, code + return + end if + + write(12, '("unknown error: ", i0)') code + end subroutine db_error +end module db diff --git a/fortran/server/test.sh b/fortran/server/test.sh new file mode 100755 index 0000000..2cfcf9f --- /dev/null +++ b/fortran/server/test.sh @@ -0,0 +1,2 @@ +#!/bin/sh +listen1 'tcp!*!1234' ~/.local/bin/fortran-micro-httpd ../../common/html/index.html ../../common/sql/test.db3 diff --git a/fortran/www/README.md b/fortran/www/README.md index e6f5c56..00cee29 100644 --- a/fortran/www/README.md +++ b/fortran/www/README.md @@ -164,7 +164,7 @@ The request/response headers are pretty easily replicated. Probably the best feature in the HTTP protocol is actually the `Content-Length` header, since it allows you to exactly allocate the amount of memory needed to store the body of the request. -There isnt a whole lot left to implement. Just getting the path to the html file and the database file from the args. +There isnt a whole lot left to implement. Just getting the path to the html file and the database file from the args. We read the index.html from the file and serve it to the front end on a `GET` request. @@ -172,3 +172,4 @@ On a `POST` request it will parse the body and insert the user data into the dat This is probably the longest I have had to take for the smallest amount of actual features for anything I have created yet. +Also the hex to integer just doesnt work at all so I had to implement one myself.... diff --git a/fortran/www/app/main.f90 b/fortran/www/app/main.f90 index 28b429f..e86dfb0 100644 --- a/fortran/www/app/main.f90 +++ b/fortran/www/app/main.f90 @@ -58,7 +58,7 @@ contains integer :: i, j, s_idx, e_idx, username_len, password_len character(len=24) :: username character(len=24) :: password - character(len=8) :: appearance + integer :: appearance_r, appearance_g, appearance_b real(kind=c_double) :: x_pos = 0.0_c_double real(kind=c_double) :: y_pos = 0.0_c_double logical :: start = .false. @@ -108,16 +108,39 @@ contains get_appearance: do i = e_idx + 2, length if (request(i) .eq. '=') then - s_idx = i + 1 + s_idx = i + 4 end if end do get_appearance - appearance = transfer(request(s_idx:length), appearance) + + appearance_r = hex2int(request(s_idx), request(s_idx+1)) + appearance_g = hex2int(request(s_idx+2), request(s_idx+3)) + appearance_b = hex2int(request(s_idx+4), request(s_idx+5)) rc = db_open(db, db_path(:Len_Trim(db_path))) rc = db_add_user(db, username(:username_len), password(:password_len), & - appearance(:Len_Trim(appearance)), x_pos, y_pos, 0, time()) + appearance_r, appearance_g, appearance_b, x_pos, y_pos, 0, time()) rc = db_close(db) end subroutine add_user + integer function hex2int(h1, h2) result(val) + character, intent(in) :: h1 + character, intent(in) :: h2 + integer :: a, b + + if (ichar(h1) .le. ichar('9')) then + a = ichar(h1) - ichar('0') + else + a = iand(ichar(h1), 7) + 9 + end if + + if (ichar(h2) .le. ichar('9')) then + b = ichar(h2) - ichar('0') + else + b = iand(ichar(h2), 7) + 9 + end if + + val = shiftl(a, 4) + b + end function hex2int + end program main diff --git a/fortran/www/fpm.toml b/fortran/www/fpm.toml index 0345f87..eecd7e5 100644 --- a/fortran/www/fpm.toml +++ b/fortran/www/fpm.toml @@ -1,6 +1,6 @@ author = "zongor" copyright = "Copyright 2023, zongor" -license = "license" +license = "MIT" maintainer = "admin@alfrescocavern.com" name = "fortran-micro-httpd" version = "0.1.0" diff --git a/fortran/www/src/db.f90 b/fortran/www/src/db.f90 index 91a8500..5cac73e 100644 --- a/fortran/www/src/db.f90 +++ b/fortran/www/src/db.f90 @@ -59,18 +59,22 @@ contains rc = db_exec(db, "CREATE TABLE users " & // "(id INTEGER PRIMARY KEY ASC, " & // "username TEXT, password TEXT, " & - // "apperance TEXT, x_pos REAL, " & - // "y_pos REAL, logged_in INTEGER, " & + // "apperance_r INTEGER, apperance_g INTEGER, apperance_b INTEGER, " & + // "x_pos REAL, y_pos REAL, logged_in INTEGER, " & // "created INTEGER);") if (rc /= SQLITE_OK) return end function db_create_users - integer function db_add_user(db, username, password, apperance, x_pos, y_pos, logged_in, created) result(rc) + integer function db_add_user(db, username, password, apperance_r, apperance_g, apperance_b, & + x_pos, y_pos, logged_in, created) result(rc) + !! Adds student to database. type(db_type), intent(inout) :: db character(len=*), intent(in) :: username character(len=*), intent(in) :: password - character(len=*), intent(in) :: apperance + integer(kind=c_int), intent(in) :: apperance_r + integer(kind=c_int), intent(in) :: apperance_g + integer(kind=c_int), intent(in) :: apperance_b real(kind=c_double), intent(in) :: x_pos real(kind=c_double), intent(in) :: y_pos integer(kind=c_int), intent(in) :: logged_in @@ -79,22 +83,26 @@ contains ! Insert values through prepared statement. rc = sqlite3_prepare_v2(db%ptr, "INSERT INTO users(username, password, " & - //"apperance, x_pos, y_pos, logged_in, created) VALUES (?,?,?,?,?,?,?)", stmt) + //"apperance_r, apperance_g, apperance_b, x_pos, y_pos, logged_in, created) VALUES (?,?,?,?,?,?,?,?,?)", stmt) call db_error(rc, 'sqlite3_prepare_v2()') rc = sqlite3_bind_text(stmt, 1, username) call db_error(rc, 'sqlite3_bind_text()') rc = sqlite3_bind_text(stmt, 2, password) call db_error(rc, 'sqlite3_bind_text()') - rc = sqlite3_bind_text(stmt, 3, apperance) - call db_error(rc, 'sqlite3_bind_text()') - rc = sqlite3_bind_double(stmt, 4, x_pos) - call db_error(rc, 'sqlite3_bind_double()') - rc = sqlite3_bind_double(stmt, 5, y_pos) - call db_error(rc, 'sqlite3_bind_double()') - rc = sqlite3_bind_int(stmt, 6, logged_in) + rc = sqlite3_bind_int(stmt, 3, apperance_r) call db_error(rc, 'sqlite3_bind_int()') - rc = sqlite3_bind_int(stmt, 7, created) + rc = sqlite3_bind_int(stmt, 4, apperance_g) + call db_error(rc, 'sqlite3_bind_int()') + rc = sqlite3_bind_int(stmt, 5, apperance_b) + call db_error(rc, 'sqlite3_bind_int()') + rc = sqlite3_bind_double(stmt, 6, x_pos) + call db_error(rc, 'sqlite3_bind_double()') + rc = sqlite3_bind_double(stmt, 7, y_pos) + call db_error(rc, 'sqlite3_bind_double()') + rc = sqlite3_bind_int(stmt, 8, logged_in) + call db_error(rc, 'sqlite3_bind_int()') + rc = sqlite3_bind_int(stmt, 9, created) call db_error(rc, 'sqlite3_bind_int()') ! Insert bound value into database.