add readme's, remove testing prints

This commit is contained in:
zongor 2023-10-13 22:55:14 -04:00
parent b44d3e03ca
commit 36579a0784
11 changed files with 108 additions and 431 deletions

View File

@ -1,2 +1,4 @@
# mmo-project
A programming chrestonomy project where various mmo systems are compared

Binary file not shown.

View File

@ -1,7 +1,8 @@
# Programming Language Project Fortran Implementation or, a Fortran CGI program abuses a sqlite3 database to implement a game server
# Fortran MMO Project Implementation or, a Frankensteins moster of a Fortran program abuses a sqlite3 database to implement a game server.
[fortran-µhttpd (game website & webserver)](./www/README.md)
server (game backend)
[fortran-mmo-server (game backend)](./server/README.md)
[fortran-mmo-client (game frontend / UI)](./client/README.md)
client (game frontend / UI)

View File

@ -1,5 +1,31 @@
# fortran mmo-project client
pipes suck and dont work, try wrapping the libdill library instead.
The client uses the same mod_dill library from the [tcp-client-server demo](https://github.com/modern-fortran/tcp-client-server)
Also the [raylib library](https://www.raylib.com/). I have generated a fortran interface for the functions neccisary for a minimal client. The module is using the new C interop interface implemented in the 2018 spec which makes it fairly easy to create a interface between a C library and Fortran.
One thing to note in here is that Fortran does not actually have unsigned values, so you have to use normal integers and use the `-fno-range-check` flag to tell the compiler that you know what you are doing. Otherwise the colors would not look correct.
For simplicity I have made it so that the login credentials are passed in through the command line instead of a GUI interface.
Fortran has the ability to use the OOP paradigm so I wanted to show that off here with the player module.
The player is able to store their own name, position, and color. There is an interface to allow the client to sync the camera and send the position to the server.
You can also see the json library shine with this implementation. It made it trivial to process the json array that was built and sent from the server.
The only other thing to note is the weird modulo chunk here:
``` fortran
time = get_time()
if (modulo(time, 1.0) .ge. 0.58_c_double) then
if (player_updated) then
players = me%move()
else
players = me%ping()
end if
end if
```
This is because there is not a good way to asyncronously send a call to the server, so I have this hack which uses the current tick time from raylib and run the update/ping function every 500 milliseconds or so.
libdill library from https://github.com/modern-fortran/tcp-client-server

View File

@ -18,20 +18,13 @@ program main
logical :: player_updated, exist
real(c_double) :: time
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
call getarg(1, username)
call getarg(2, password)
me = player(username, vector3(0.0_c_float, 1.0_c_float, 2.0_c_float), PURPLE)
players = me%login(password)
camera%position = vector3(me%position%x,me%position%y + 10.0_c_float , me%position%z + 10.0_c_float ) !Camera position
camera%position = vector3(me%position%x,me%position%y + 10.0_c_float ,me%position%z + 10.0_c_float) !Camera position
camera%target = vector3(me%position%x,me%position%y,me%position%z) !Camera looking at point
camera%up = vector3(0.0_c_float, 1.0_c_float, 0.0_c_float) !Camera up vector(rotation towards target)
camera%fovy = 45.0_c_float !Camera field - of - view Y
@ -60,7 +53,7 @@ program main
call me%sync_camera(camera)
time = get_time()
if (modulo(time, 1.0) .ge. 0.98_c_double) then
if (modulo(time, 1.0) .ge. 0.58_c_double) then
if (player_updated) then
players = me%move()
else
@ -89,6 +82,5 @@ program main
deallocate (players)
end if
call close_window() !Close window and OpenGL context
close (12)
end program main

View File

@ -36,8 +36,6 @@ contains
character(24) :: password
type(player), dimension(:), allocatable :: players
print *, password
players = send_packet(this, 1)
end function login
@ -115,7 +113,7 @@ contains
call json%add(root, user)
call json%serialize(root, str)
call json%print(root)
! call json%print(root)
connection = suffix_attach(connection, TCP_SUFFIX, 2_c_size_t)
@ -128,7 +126,7 @@ contains
message_size = mrecv(connection, message, msglen, -1_c_int64_t)
print *, 'message=', message
! print *, 'message=', message
call c_f_string(message, jsn_string)

View File

@ -20,31 +20,7 @@ module raylib
integer(c_int8_t) :: a
end type
type(color) :: LIGHTGRAY = color(200, 200, 200, 255)
type(color) :: GRAY = color(130, 130, 130, 255)
type(color) :: DARKGRAY = color(80, 80, 80, 255)
type(color) :: YELLOW = color(253, 249, 0, 255)
type(color) :: GOLD = color(255, 203, 0, 255)
type(color) :: ORANGE = color(255, 161, 0, 255)
type(color) :: PINK = color(255, 109, 194, 255)
type(color) :: RED = color(230, 41, 55, 255)
type(color) :: MAROON = color(190, 33, 55, 255)
type(color) :: GREEN = color(0, 228, 48, 255)
type(color) :: LIME = color(0, 158, 47, 255)
type(color) :: DARKGREEN = color(0, 117, 44, 255)
type(color) :: SKYBLUE = color(102, 191, 255, 255)
type(color) :: BLUE = color(0, 121, 241, 255)
type(color) :: DARKBLUE = color(0, 82, 172, 255)
type(color) :: PURPLE = color(200, 122, 255, 255)
type(color) :: VIOLET = color(135, 60, 190, 255)
type(color) :: DARKPURPLE = color(112, 31, 126, 255)
type(color) :: BEIGE = color(211, 176, 131, 255)
type(color) :: BROWN = color(127, 106, 79, 255)
type(color) :: DARKBROWN = color(76, 63, 47, 255)
type(color) :: WHITE = color(255, 255, 255, 255)
type(color) :: BLACK = color(0, 0, 0, 255)
type(color) :: BLANK = color(0, 0, 0, 0)
type(color) :: MAGENTA = color(255, 0, 255, 255)
type(color) :: RAYWHITE = color(245, 245, 245, 255)
type, bind(c) :: camera3d
@ -55,297 +31,12 @@ module raylib
integer(c_int) :: projection
end type
! Keyboard keys
integer(c_int) :: KEY_NULL = 0
! Alphanumeric keys
integer(c_int) :: KEY_APOSTROPHE = 39
integer(c_int) :: KEY_COMMA = 44
integer(c_int) :: KEY_MINUS = 45
integer(c_int) :: KEY_PERIOD = 46
integer(c_int) :: KEY_SLASH = 47
integer(c_int) :: KEY_ZERO = 48
integer(c_int) :: KEY_ONE = 49
integer(c_int) :: KEY_TWO = 50
integer(c_int) :: KEY_THREE = 51
integer(c_int) :: KEY_FOUR = 52
integer(c_int) :: KEY_FIVE = 53
integer(c_int) :: KEY_SIX = 54
integer(c_int) :: KEY_SEVEN = 55
integer(c_int) :: KEY_EIGHT = 56
integer(c_int) :: KEY_NINE = 57
integer(c_int) :: KEY_SEMICOLON = 59
integer(c_int) :: KEY_EQUAL = 61
integer(c_int) :: KEY_A = 65
integer(c_int) :: KEY_B = 66
integer(c_int) :: KEY_C = 67
integer(c_int) :: KEY_D = 68
integer(c_int) :: KEY_E = 69
integer(c_int) :: KEY_F = 70
integer(c_int) :: KEY_G = 71
integer(c_int) :: KEY_H = 72
integer(c_int) :: KEY_I = 73
integer(c_int) :: KEY_J = 74
integer(c_int) :: KEY_K = 75
integer(c_int) :: KEY_L = 76
integer(c_int) :: KEY_M = 77
integer(c_int) :: KEY_N = 78
integer(c_int) :: KEY_O = 79
integer(c_int) :: KEY_P = 80
integer(c_int) :: KEY_Q = 81
integer(c_int) :: KEY_R = 82
integer(c_int) :: KEY_S = 83
integer(c_int) :: KEY_T = 84
integer(c_int) :: KEY_U = 85
integer(c_int) :: KEY_V = 86
integer(c_int) :: KEY_W = 87
integer(c_int) :: KEY_X = 88
integer(c_int) :: KEY_Y = 89
integer(c_int) :: KEY_Z = 90
! Function keys
integer(c_int) :: KEY_SPACE = 32
integer(c_int) :: KEY_ESCAPE = 256
integer(c_int) :: KEY_ENTER = 257
integer(c_int) :: KEY_TAB = 258
integer(c_int) :: KEY_BACKSPACE = 259
integer(c_int) :: KEY_INSERT = 260
integer(c_int) :: KEY_DELETE = 261
integer(c_int) :: KEY_RIGHT = 262
integer(c_int) :: KEY_LEFT = 263
integer(c_int) :: KEY_DOWN = 264
integer(c_int) :: KEY_UP = 265
integer(c_int) :: KEY_PAGE_UP = 266
integer(c_int) :: KEY_PAGE_DOWN = 267
integer(c_int) :: KEY_HOME = 268
integer(c_int) :: KEY_END = 269
integer(c_int) :: KEY_CAPS_LOCK = 280
integer(c_int) :: KEY_SCROLL_LOCK = 281
integer(c_int) :: KEY_NUM_LOCK = 282
integer(c_int) :: KEY_PRINT_SCREEN = 283
integer(c_int) :: KEY_PAUSE = 284
integer(c_int) :: KEY_F1 = 290
integer(c_int) :: KEY_F2 = 291
integer(c_int) :: KEY_F3 = 292
integer(c_int) :: KEY_F4 = 293
integer(c_int) :: KEY_F5 = 294
integer(c_int) :: KEY_F6 = 295
integer(c_int) :: KEY_F7 = 296
integer(c_int) :: KEY_F8 = 297
integer(c_int) :: KEY_F9 = 298
integer(c_int) :: KEY_F10 = 299
integer(c_int) :: KEY_F11 = 300
integer(c_int) :: KEY_F12 = 301
integer(c_int) :: KEY_LEFT_SHIFT = 340
integer(c_int) :: KEY_LEFT_CONTROL = 341
integer(c_int) :: KEY_LEFT_ALT = 342
integer(c_int) :: KEY_LEFT_SUPER = 343
integer(c_int) :: KEY_RIGHT_SHIFT = 344
integer(c_int) :: KEY_RIGHT_CONTROL = 345
integer(c_int) :: KEY_RIGHT_ALT = 346
integer(c_int) :: KEY_RIGHT_SUPER = 347
integer(c_int) :: KEY_KB_MENU = 348
integer(c_int) :: KEY_LEFT_BRACKET = 91
integer(c_int) :: KEY_BACKSLASH = 92
integer(c_int) :: KEY_RIGHT_BRACKET = 93
integer(c_int) :: KEY_GRAVE = 96
! Keypad keys
integer(c_int) :: KEY_KP_0 = 320
integer(c_int) :: KEY_KP_1 = 321
integer(c_int) :: KEY_KP_2 = 322
integer(c_int) :: KEY_KP_3 = 323
integer(c_int) :: KEY_KP_4 = 324
integer(c_int) :: KEY_KP_5 = 325
integer(c_int) :: KEY_KP_6 = 326
integer(c_int) :: KEY_KP_7 = 327
integer(c_int) :: KEY_KP_8 = 328
integer(c_int) :: KEY_KP_9 = 329
integer(c_int) :: KEY_KP_DECIMAL = 330
integer(c_int) :: KEY_KP_DIVIDE = 331
integer(c_int) :: KEY_KP_MULTIPLY = 332
integer(c_int) :: KEY_KP_SUBTRACT = 333
integer(c_int) :: KEY_KP_ADD = 334
integer(c_int) :: KEY_KP_ENTER = 335
integer(c_int) :: KEY_KP_EQUAL = 336
! Android key buttons
integer(c_int) :: KEY_BACK = 4
integer(c_int) :: KEY_MENU = 82
integer(c_int) :: KEY_VOLUME_UP = 24
integer(c_int) :: KEY_VOLUME_DOWN = 25
! Mouse buttons
integer(c_int) :: MOUSE_LEFT_BUTTON = 0
integer(c_int) :: MOUSE_RIGHT_BUTTON = 1
integer(c_int) :: MOUSE_MIDDLE_BUTTON = 2
! Mouse cursor
integer(c_int) :: MOUSE_CURSOR_DEFAULT = 0
integer(c_int) :: MOUSE_CURSOR_ARROW = 1
integer(c_int) :: MOUSE_CURSOR_IBEAM = 2
integer(c_int) :: MOUSE_CURSOR_CROSSHAIR = 3
integer(c_int) :: MOUSE_CURSOR_POINTING_HAND = 4
integer(c_int) :: MOUSE_CURSOR_RESIZE_EW = 5
integer(c_int) :: MOUSE_CURSOR_RESIZE_NS = 6
integer(c_int) :: MOUSE_CURSOR_RESIZE_NWSE = 7
integer(c_int) :: MOUSE_CURSOR_RESIZE_NESW = 8
integer(c_int) :: MOUSE_CURSOR_RESIZE_ALL = 9
integer(c_int) :: MOUSE_CURSOR_NOT_ALLOWED = 10
! Gamepad buttons
integer(c_int) :: GAMEPAD_BUTTON_UNKNOWN = 0
! This is normally a DPAD
integer(c_int) :: GAMEPAD_BUTTON_LEFT_FACE_UP = 1
integer(c_int) :: GAMEPAD_BUTTON_LEFT_FACE_RIGHT = 2
integer(c_int) :: GAMEPAD_BUTTON_LEFT_FACE_DOWN = 3
integer(c_int) :: GAMEPAD_BUTTON_LEFT_FACE_LEFT = 4
! This normally corresponds with PlayStation and Xbox controllers
! XBOX: [Y,X,A,B]
! PS3: [Triangle,Square,Cross,Circle]
! No support for 6 button controllers though..
integer(c_int) :: GAMEPAD_BUTTON_RIGHT_FACE_UP = 5
integer(c_int) :: GAMEPAD_BUTTON_RIGHT_FACE_RIGHT = 6
integer(c_int) :: GAMEPAD_BUTTON_RIGHT_FACE_DOWN = 7
integer(c_int) :: GAMEPAD_BUTTON_RIGHT_FACE_LEFT = 8
integer(c_int) :: GAMEPAD_BUTTON_LEFT_TRIGGER_1 = 9
integer(c_int) :: GAMEPAD_BUTTON_LEFT_TRIGGER_2 = 10
integer(c_int) :: GAMEPAD_BUTTON_RIGHT_TRIGGER_1 = 11
integer(c_int) :: GAMEPAD_BUTTON_RIGHT_TRIGGER_2 = 12
integer(c_int) :: GAMEPAD_BUTTON_MIDDLE_LEFT = 13 ! PS3 Select
integer(c_int) :: GAMEPAD_BUTTON_MIDDLE = 14 ! PS Button/XBOX Button
integer(c_int) :: GAMEPAD_BUTTON_MIDDLE_RIGHT = 15 ! PS3 Start
! These are the joystick press in buttons
integer(c_int) :: GAMEPAD_BUTTON_LEFT_THUMB = 16
integer(c_int) :: GAMEPAD_BUTTON_RIGHT_THUMB = 17
! Gamepad axis
integer(c_int) :: GAMEPAD_AXIS_LEFT_X = 0
integer(c_int) :: GAMEPAD_AXIS_LEFT_Y = 1
integer(c_int) :: GAMEPAD_AXIS_RIGHT_X = 2
integer(c_int) :: GAMEPAD_AXIS_RIGHT_Y = 3
integer(c_int) :: GAMEPAD_AXIS_LEFT_TRIGGER = 4 ! [1..-1] (pressure-level)
integer(c_int) :: GAMEPAD_AXIS_RIGHT_TRIGGER = 5 ! [1..-1] (pressure-level)
! Material map index
integer(c_int) :: MATERIAL_MAP_ALBEDO = 0
integer(c_int) :: MATERIAL_MAP_DIFFUSE = 0 ! same as MATERIAL_MAP_ALBEDO
integer(c_int) :: MATERIAL_MAP_METALNESS = 1
integer(c_int) :: MATERIAL_MAP_SPECULAR = 1 ! same as MATERIAL_MAP_METALNESS
integer(c_int) :: MATERIAL_MAP_NORMAL = 2
integer(c_int) :: MATERIAL_MAP_ROUGHNESS = 3
integer(c_int) :: MATERIAL_MAP_OCCLUSION = 4
integer(c_int) :: MATERIAL_MAP_EMISSION = 5
integer(c_int) :: MATERIAL_MAP_HEIGHT = 6
integer(c_int) :: MATERIAL_MAP_BRDG = 7
integer(c_int) :: MATERIAL_MAP_CUBEMAP = 8 ! NOTE: Uses GL_TEXTURE_CUBE_MAP
integer(c_int) :: MATERIAL_MAP_IRRADIANCE = 9 ! NOTE: Uses GL_TEXTURE_CUBE_MAP
integer(c_int) :: MATERIAL_MAP_PREFILTER = 10 ! NOTE: Uses GL_TEXTURE_CUBE_MAP
! Shader location index
integer(c_int) :: SHADER_LOC_VERTEX_POSITION = 0
integer(c_int) :: SHADER_LOC_VERTEX_TEXCOORD01 = 1
integer(c_int) :: SHADER_LOC_VERTEX_TEXCOORD02 = 2
integer(c_int) :: SHADER_LOC_VERTEX_NORMAL = 3
integer(c_int) :: SHADER_LOC_VERTEX_TANGENT = 4
integer(c_int) :: SHADER_LOC_VERTEX_COLOR = 5
integer(c_int) :: SHADER_LOC_MATRIX_MVP = 6
integer(c_int) :: SHADER_LOC_MATRIX_VIEW = 7
integer(c_int) :: SHADER_LOC_MATRIX_PROJECTION = 8
integer(c_int) :: SHADER_LOC_MATRIX_MODEL = 9
integer(c_int) :: SHADER_LOC_MATRIX_NORMAL = 10
integer(c_int) :: SHADER_LOC_VECTOR_VIEW = 11
integer(c_int) :: SHADER_LOC_COLOR_DIFFUSE = 12
integer(c_int) :: SHADER_LOC_COLOR_SPECULAR = 13
integer(c_int) :: SHADER_LOC_COLOR_AMBIENT = 14
integer(c_int) :: SHADER_LOC_MAP_ALBEDO = 15
integer(c_int) :: SHADER_LOC_MAP_DIFFUSE = 15 ! same as SHADER_LOC_MAP_ALBEDO
integer(c_int) :: SHADER_LOC_MAP_METALNESS = 16
integer(c_int) :: SHADER_LOC_MAP_SPECULAR = 16 ! same as SHADER_LOC_MAP_METALNESS
integer(c_int) :: SHADER_LOC_MAP_NORMAL = 17
integer(c_int) :: SHADER_LOC_MAP_ROUGHNESS = 18
integer(c_int) :: SHADER_LOC_MAP_OCCLUSION = 19
integer(c_int) :: SHADER_LOC_MAP_EMISSION = 20
integer(c_int) :: SHADER_LOC_MAP_HEIGHT = 21
integer(c_int) :: SHADER_LOC_MAP_CUBEMAP = 22
integer(c_int) :: SHADER_LOC_MAP_IRRADIANCE = 23
integer(c_int) :: SHADER_LOC_MAP_PREFILTER = 24
integer(c_int) :: SHADER_LOC_MAP_BRDF = 25
! Shader uniform data type
integer(c_int) :: SHADER_UNIFORM_FLOAT = 0
integer(c_int) :: SHADER_UNIFORM_VEC2 = 1
integer(c_int) :: SHADER_UNIFORM_VEC3 = 2
integer(c_int) :: SHADER_UNIFORM_VEC4 = 3
integer(c_int) :: SHADER_UNIFORM_INT = 4
integer(c_int) :: SHADER_UNIFORM_IVEC2 = 5
integer(c_int) :: SHADER_UNIFORM_IVEC3 = 6
integer(c_int) :: SHADER_UNIFORM_IVEC4 = 7
integer(c_int) :: SHADER_UNIFORM_SAMPLER2D = 8
! Pixel formats
integer(c_int) :: PIXELFORMAT_UNCOMPRESSED_GRAYSCALE = 1 ! 8 bit per pixel (no alpha)
integer(c_int) :: PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA = 2 ! 8*2 bpp (2 channels)
integer(c_int) :: PIXELFORMAT_UNCOMPRESSED_R5G6B5 = 3 ! 16 bpp
integer(c_int) :: PIXELFORMAT_UNCOMPRESSED_R8G8B8 = 4 ! 24 bpp
integer(c_int) :: PIXELFORMAT_UNCOMPRESSED_R5G5B5A1 = 5 ! 16 bpp (1 bit alpha)
integer(c_int) :: PIXELFORMAT_UNCOMPRESSED_R4G4B4A4 = 6 ! 16 bpp (4 bit alpha)
integer(c_int) :: PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 = 7 ! 32 bpp
integer(c_int) :: PIXELFORMAT_UNCOMPRESSED_R32 = 8 ! 32 bpp (1 channel - float)
integer(c_int) :: PIXELFORMAT_UNCOMPRESSED_R32G32B32 = 9 ! 32*3 bpp (3 channels - float)
integer(c_int) :: PIXELFORMAT_UNCOMPRESSED_R32G32B32A32 = 10 ! 32*4 bpp (4 channels - float)
integer(c_int) :: PIXELFORMAT_COMPRESSED_DXT1_RGB = 11 ! 4 bpp (no alpha)
integer(c_int) :: PIXELFORMAT_COMPRESSED_DXT1_RGBA = 12 ! 4 bpp (1 bit alpha)
integer(c_int) :: PIXELFORMAT_COMPRESSED_DXT3_RGBA = 13 ! 8 bpp
integer(c_int) :: PIXELFORMAT_COMPRESSED_DXT5_RGBA = 14 ! 8 bpp
integer(c_int) :: PIXELFORMAT_COMPRESSED_ETC1_RGB = 15 ! 4 bpp
integer(c_int) :: PIXELFORMAT_COMPRESSED_ETC2_RGB = 16 ! 4 bpp
integer(c_int) :: PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA = 17 ! 8 bpp
integer(c_int) :: PIXELFORMAT_COMPRESSED_PVRT_RGB = 18 ! 4 bpp
integer(c_int) :: PIXELFORMAT_COMPRESSED_PVRT_RGBA = 19 ! 4 bpp
integer(c_int) :: PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA = 20 ! 8 bpp
integer(c_int) :: PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA = 21 ! 2 bpp
! Texture parameters: filter mode
integer(c_int) :: TEXTURE_FILTER_POINT = 0 ! No filter, just pixel aproximation
integer(c_int) :: TEXTURE_FILTER_BILINEAR = 1 ! Linear filtering
integer(c_int) :: TEXTURE_FILTER_TRILINEAR = 2 ! Trilinear filtering (linear with mipmaps)
integer(c_int) :: TEXTURE_FILTER_ANISOTROPIC_4X = 3 ! Anisotropic filtering 4x
integer(c_int) :: TEXTURE_FILTER_ANISOTROPIC_8X = 4 ! Anisotropic filtering 8x
integer(c_int) :: TEXTURE_FILTER_ANISOTROPIC_16X = 5 ! Anisotropic filtering 16x
! Texture parameters: wrap mode
integer(c_int) :: TEXTURE_WRAP_REPEAT = 0 ! Repeats texture in tiled mode
integer(c_int) :: TEXTURE_WRAP_CLAMP = 1 ! Clamps texture to edge pixel in tiled mode
integer(c_int) :: TEXTURE_WRAP_MIRROR_REPEAT = 2 ! Mirrors and repeats the texture in tiled mode
integer(c_int) :: TEXTURE_WRAP_MIRROR_CLAMP = 3 ! Mirrors and clamps to border the texture in tiled mode
! Cubemap layouts
integer(c_int) :: CUBEMAP_LAYOUT_AUTO_DETECT = 0 ! Automatically detect layout type
integer(c_int) :: CUBEMAP_LAYOUT_LINE_VERTICAL = 1 ! Layout is defined by a vertical line with faces
integer(c_int) :: CUBEMAP_LAYOUT_LINE_HORIZONTAL = 2 ! Layout is defined by an horizontal line with faces
integer(c_int) :: CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR = 3 ! Layout is defined by a 3x4 cross with cubemap faces
integer(c_int) :: CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE = 4 ! Layout is defined by a 4x3 cross with cubemap faces
integer(c_int) :: CUBEMAP_LAYOUT_PANORAMA = 5 ! Layout is defined by a panorama image (equirectangular map)
! Font type, defines generation method
integer(c_int) :: FONT_DEFAULT = 0 ! Default font generation, anti-aliased
integer(c_int) :: FONT_BITMAP = 1 ! Bitmap font generation, no anti-aliasing
integer(c_int) :: FONT_SDF = 2 ! SDF font generation, requires external shader
! Color blending modes (pre-defined)
integer(c_int) :: BLEND_ALPHA = 0 ! Blend textures considering alpha (default)
integer(c_int) :: BLEND_ADDITIVE = 1 ! Blend textures adding colors
integer(c_int) :: BLEND_MULTIPLIED = 2 ! Blend textures multiplying colors
integer(c_int) :: BLEND_ADD_COLORS = 3 ! Blend textures adding colors (alternative)
integer(c_int) :: BLEND_SUBTRACT_COLORS = 4 ! Blend textures subtracting colors (alternative)
integer(c_int) :: BLEND_CUSTOM = 5 ! Belnd textures using custom src/dst factors (use rlSetBlendMode())
! Gestures
integer(c_int) :: GESTURE_NONE = 0
integer(c_int) :: GESTURE_TAP = 1
integer(c_int) :: GESTURE_DOUBLETAP = 2
integer(c_int) :: GESTURE_HOLD = 4
integer(c_int) :: GESTURE_DRAG = 8
integer(c_int) :: GESTURE_SWIPE_RIGHT = 16
integer(c_int) :: GESTURE_SWIPE_LEFT = 32
integer(c_int) :: GESTURE_SWIPE_UP = 64
integer(c_int) :: GESTURE_SWIPE_DOWN = 128
integer(c_int) :: GESTURE_PINCH_IN = 256
integer(c_int) :: GESTURE_PINCH_OUT = 512
!Camera system modes
integer(c_int) :: CAMERA_CUSTOM = 0
integer(c_int) :: CAMERA_FREE = 1
integer(c_int) :: CAMERA_ORBITAL = 2
integer(c_int) :: CAMERA_FIRST_PERSON = 3
integer(c_int) :: CAMERA_THIRD_PERSON = 4
!Camera projection
integer(c_int) :: CAMERA_PERSPECTIVE = 0
integer(c_int) :: CAMERA_ORTHOGRAPHIC = 1
interface
@ -389,31 +80,6 @@ module raylib
integer(c_int), intent(in), value :: fps
end subroutine
subroutine update_camera(camera_ptr, mode) bind(c, name="UpdateCamera")
import :: c_ptr, c_int
type(c_ptr) :: camera_ptr
integer(c_int):: mode
end subroutine update_camera
subroutine show_cursor() bind(c, name="ShowCursor")
end subroutine show_cursor
subroutine hide_cursor() bind(c, name="HideCursor")
end subroutine hide_cursor
subroutine EnableCursor() bind(c, name="EnableCursor")
end subroutine EnableCursor
subroutine disable_cursor() bind(c, name="DisableCursor")
end subroutine disable_cursor
function is_key_pressed(key) result(res) bind(c, name="IsKeyPressed")
import :: c_int
import :: c_bool
logical(c_bool) :: res
integer(c_int), intent(in), value :: key
end function is_key_pressed
function is_key_down(key) result(res) bind(c, name="IsKeyDown")
import :: c_int
import :: c_bool
@ -421,20 +87,6 @@ module raylib
integer(c_int), intent(in), value :: key
end function is_key_down
function is_key_released(key) result(res) bind(c, name="IsKeyReleased")
import :: c_int
import :: c_bool
logical(c_bool) :: res
integer(c_int), intent(in), value :: key
end function is_key_released
function is_key_up(key) result(res) bind(c, name="IsKeyUp")
import :: c_int
import :: c_bool
logical(c_bool) :: res
integer(c_int), intent(in), value :: key
end function is_key_up
subroutine draw_grid(slices, spacing) bind(c, name="DrawGrid")
import :: c_int
import :: c_float
@ -442,17 +94,6 @@ module raylib
real(c_float), intent(in), value :: spacing
end subroutine draw_grid
subroutine draw_text(text, posX, posY, fontSize, col) bind(c, name="DrawText")
import :: c_int
import :: color
import :: c_char
character(c_char), dimension(*), intent(in) :: text
integer(c_int), intent(in), value :: posX
integer(c_int), intent(in), value :: posY
integer(c_int), intent(in), value :: fontSize
type(color), intent(in), value :: col
end subroutine draw_text
subroutine draw_cube(position, width, height, length, col) bind(c, name="DrawCube")
import :: vector3
import :: c_float
@ -464,26 +105,6 @@ module raylib
type(color), intent(in), value :: col
end subroutine draw_cube
subroutine draw_cube_wires(position, width, height, length, col) bind(c, name="DrawCubeWires")
import :: vector3
import :: c_float
import :: color
type(vector3), intent(in), value :: position
real(c_float), intent(in), value :: width
real(c_float), intent(in), value :: height
real(c_float), intent(in), value :: length
type(color), intent(in), value :: col
end subroutine draw_cube_wires
subroutine draw_plane(centerPos, size, col) bind(c, name="DrawPlane")
import vector3
import vector2
import color
type(vector3), intent(in), value :: centerPos
type(vector2), intent(in), value :: size
type(color), intent(in), value :: col
end subroutine draw_plane
function get_time() result(res) bind(c, name="GetTime")
import :: c_double
real(c_double) :: res

View File

@ -1,7 +1,16 @@
# server
- request
The server implementation is as simple as I could make it.
I found a library called mod_dill from the [tcp-client-server demo code](https://github.com/modern-fortran/tcp-client-server) from a book on modern fortran. This gave me a simple TCP interface to send and recieve "messages".
Originally I had tried saving formatted fortran data into a string and trying to send that, but when you convert the fortran string to a c-string it oftend became garbled and unusable when sent through the tcp connection so I stopped using that method.
I found in [fpm](https://fpm.fortran-lang.org/index.html) [official registry](https://registry-frontend.vercel.app/) a json library which would make it *much* easier to deal with data transfer.
The simplest way to implement the communcation between the server and clients would be a series of tcp requests and responses.
- request
- int :: request_type
- ping # 0
- login # 1
@ -12,10 +21,23 @@
- 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
For each request, the same response is returned with is a list of the logged in users.
The commands that can be sent to the server are logging in, logging out, moving, and the default "ping" which is essentially a noop from the servers pov.
The server uses the same sqlite library as the [www implementation](../www/README.md).
The server loop is:
1. listen for connection
2. read message from client
3. convert c string to fortran string
4. convert the fortran string to a json object
5. run the command from the json object
6. create a json array of the logged in users and send to client.

View File

@ -52,17 +52,17 @@ program main
connection = tcp_accept(socket, addr_remote, -1_c_int64_t)
call ipaddr_str(addr, address_string)
print *, 'New connection from '//trim(address_string)
! print *, 'New connection from '//trim(address_string)
connection = suffix_attach(connection, TCP_SUFFIX, 2_c_size_t)
print *, 'connected'
! print *, 'connected'
message_size = mrecv(connection, message, msglen, -1_c_int64_t)
print *, message_size
! print *, message_size
call c_f_string(message, jsn_string)
call json%initialize()
call json%deserialize(jsn_string(:Len_Trim(jsn_string)))
call json%print()
! call json%print()
call json%get('user.username', username, found)
if (.not. found) print *, 'cant find username!'

View File

@ -198,8 +198,8 @@ contains
call json%add(root, users)
call json%serialize(root, str)
print *, 'sending users'
call json%print(root)
! print *, 'sending users'
! call json%print(root)
rc = msend(connection, f_c_string(str, .true.), &
transfer(Len_Trim(f_c_string(str, .true.)), 0_c_size_t), -1_c_int64_t)

View File

@ -29,9 +29,7 @@ I next implemented the post handling. In www.f90, it checks to see if the header
I found a library to interface with the sqlite library I will store the username, password, and other information about.
The issue is that fortran does not have a built in cryptographic hash library nor is it in the stdlib, I did find a implementation of the [SM3](https://github.com/zoziha/SM3-Fortran/tree/main) hash which is cryptographically sound, and have used that.
If this were to be used in the real world I would prefer to use [bcrypt](https://en.wikipedia.org/wiki/Bcrypt) as it is the most common password hashing algorithm.
The issue is that fortran does not have a built in cryptographic hash library nor is it in the stdlib. So it has been ommited for this implementation, although it should not be in a real world situation.
The implementation does not work well with existing systems since you have to convert from fortran strings to c strings and it returns an integer array of c strings which has to be converted back into fortran strings.
@ -44,18 +42,18 @@ Speaking of strings. At first fortran luls you into a false sense of security wi
For example, this is totaly valid in fortran:
```fortran
character, dimension(:), allocatable :: tape
character, dimension(4) :: magic_module_header = [ achar(0), achar(97), achar(115), achar(109) ]
character, dimension(4) :: module_version = [ achar(1), achar(0), achar(0), achar(0) ]
character, dimension(:), allocatable :: tape
character, dimension(4) :: magic_module_header = [ achar(0), achar(97), achar(115), achar(109) ]
character, dimension(4) :: module_version = [ achar(1), achar(0), achar(0), achar(0) ]
tape = [magic_module_header]
tape = [tape, module_version]
tape = [magic_module_header]
tape = [tape, module_version]
open(unit=u, file=file_output, access='stream', status='replace', action='write', iostat=ios)
write(u, iostat=ios) tape
close(u, iostat=ios)
open(unit=u, file=file_output, access='stream', status='replace', action='write', iostat=ios)
write(u, iostat=ios) tape
close(u, iostat=ios)
deallocate(tape)
deallocate(tape)
```
Pretty cool for a language with similar performance to C!
@ -63,21 +61,21 @@ Pretty cool for a language with similar performance to C!
Well, its not that simple when it comes to "strings" because there are different ways to structure a "string" in fortran.
```fortran
! theres the 'suggested fortran 2023 standard way'
! this format is also the way you can use a lot of the built in string functions and the '//' append operator.
character(len=:), allocatable :: string
! theres the 'suggested fortran 2023 standard way'
! this format is also the way you can use a lot of the built in string functions and the '//' append operator.
character(len=:), allocatable :: string
! this way which is useful since you can use the same interface as a normal 1d array
character, dimension(:), allocatable :: string
! this way which is useful since you can use the same interface as a normal 1d array
character, dimension(:), allocatable :: string
! this other other way which only works inside of subrotines/functions
character, intent(in) :: string(:)
! this other other way which only works inside of subrotines/functions
character, intent(in) :: string(:)
! also this older way
character(*) :: string
! also this older way
character(*) :: string
! and this ancent '77 spec way which wont work in some compiling modes.
CHARACTER string*17
! and this ancent '77 spec way which wont work in some compiling modes.
CHARACTER string*17
```
Can you tell the difference between all of them? theyre all doing almost the same thing, but they are slightly different. And some ways seem to work fine until you try to write to output. Or if you use a speicifc operator on one tyep, you cant on the other.
@ -172,4 +170,21 @@ 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....
Also the hex to integer just doesnt work at all so I had to implement one myself...
The next issue was SQL. There is no native interface with Fortran and SQLite3 (which is the databse I am using for this project). The only "native" soluition would be to use the `execute_command_line` subroutine and then read the output from a temp file in order to do sql operations.
So I looked around on the internet for a different solution.
To my supprise I found a project that aims to add a package manager to Fortran called [fpm](https://fpm.fortran-lang.org/index.html)
One of the libraries that was available was a sqlite implementation. So I decided that since I was already a month behind schedule I would stop being so strict with my rule of wanting to use "100% vanilla Fortran" and start using this package manager.
The Fortran package manger is actually pretty fantastic. It has a similar feel to [npm](https://www.npmjs.com) if you have expereince with that.
Creating a new project is as easy as `fpm new Project_Name` and adding libraries can be done by editing the .toml file and running `fpm build`
The specific library I found for sqlite is [fortran-sqlite3](https://github.com/interkosmos/fortran-sqlite3).
So I finished up the last few things in the implementation and called it complete.