diff --git a/README.md b/README.md index f095f67..b3643b0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # mmo-project +A programming chrestonomy project where various mmo systems are compared + diff --git a/common/sql/test.db3 b/common/sql/test.db3 index 351cb55..07210a4 100644 Binary files a/common/sql/test.db3 and b/common/sql/test.db3 differ diff --git a/fortran/README.md b/fortran/README.md index 198d885..064e0a5 100644 --- a/fortran/README.md +++ b/fortran/README.md @@ -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) diff --git a/fortran/client/README.md b/fortran/client/README.md index 31c4cea..9c28642 100644 --- a/fortran/client/README.md +++ b/fortran/client/README.md @@ -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 diff --git a/fortran/client/app/main.f90 b/fortran/client/app/main.f90 index 530ce9d..fe84477 100644 --- a/fortran/client/app/main.f90 +++ b/fortran/client/app/main.f90 @@ -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 diff --git a/fortran/client/src/player.f90 b/fortran/client/src/player.f90 index bac4d0d..f03d20e 100644 --- a/fortran/client/src/player.f90 +++ b/fortran/client/src/player.f90 @@ -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) diff --git a/fortran/client/src/raylib.f90 b/fortran/client/src/raylib.f90 index c295c28..fd3e70f 100644 --- a/fortran/client/src/raylib.f90 +++ b/fortran/client/src/raylib.f90 @@ -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 diff --git a/fortran/server/README.md b/fortran/server/README.md index 9ed5f51..fcee21c 100644 --- a/fortran/server/README.md +++ b/fortran/server/README.md @@ -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. + diff --git a/fortran/server/app/main.f90 b/fortran/server/app/main.f90 index c7fb0e5..c5373c3 100644 --- a/fortran/server/app/main.f90 +++ b/fortran/server/app/main.f90 @@ -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!' diff --git a/fortran/server/src/db.f90 b/fortran/server/src/db.f90 index fabe862..b2e8a21 100644 --- a/fortran/server/src/db.f90 +++ b/fortran/server/src/db.f90 @@ -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) diff --git a/fortran/www/README.md b/fortran/www/README.md index 00cee29..efe4410 100644 --- a/fortran/www/README.md +++ b/fortran/www/README.md @@ -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. +