added manyloc test suite; --path now relative to project dir if not absolute
This commit is contained in:
parent
4f09794be9
commit
75b508032b
51 changed files with 9286 additions and 5 deletions
|
|
@ -196,8 +196,10 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
|
||||||
of "patterns": result = contains(gOptions, optPatterns)
|
of "patterns": result = contains(gOptions, optPatterns)
|
||||||
else: InvalidCmdLineOption(passCmd1, switch, info)
|
else: InvalidCmdLineOption(passCmd1, switch, info)
|
||||||
|
|
||||||
proc processPath(path: string): string =
|
proc processPath(path: string, notRelativeToProj = false): string =
|
||||||
result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath,
|
let p = if notRelativeToProj or os.isAbsolute(path) or '$' in path: path
|
||||||
|
else: options.gProjectPath / path
|
||||||
|
result = UnixToNativePath(p % ["nimrod", getPrefixDir(), "lib", libpath,
|
||||||
"home", removeTrailingDirSep(os.getHomeDir()),
|
"home", removeTrailingDirSep(os.getHomeDir()),
|
||||||
"projectname", options.gProjectName,
|
"projectname", options.gProjectName,
|
||||||
"projectpath", options.gProjectPath])
|
"projectpath", options.gProjectPath])
|
||||||
|
|
@ -229,7 +231,7 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
|
||||||
of "babelpath":
|
of "babelpath":
|
||||||
if pass in {passCmd2, passPP}:
|
if pass in {passCmd2, passPP}:
|
||||||
expectArg(switch, arg, pass, info)
|
expectArg(switch, arg, pass, info)
|
||||||
let path = processPath(arg)
|
let path = processPath(arg, notRelativeToProj=true)
|
||||||
babelpath(path, info)
|
babelpath(path, info)
|
||||||
of "excludepath":
|
of "excludepath":
|
||||||
expectArg(switch, arg, pass, info)
|
expectArg(switch, arg, pass, info)
|
||||||
|
|
@ -453,7 +455,7 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
|
||||||
incl(gGlobalOptions, optGenScript)
|
incl(gGlobalOptions, optGenScript)
|
||||||
of "lib":
|
of "lib":
|
||||||
expectArg(switch, arg, pass, info)
|
expectArg(switch, arg, pass, info)
|
||||||
libpath = processPath(arg)
|
libpath = processPath(arg, notRelativeToProj=true)
|
||||||
of "putenv":
|
of "putenv":
|
||||||
expectArg(switch, arg, pass, info)
|
expectArg(switch, arg, pass, info)
|
||||||
splitSwitch(arg, key, val, pass, info)
|
splitSwitch(arg, key, val, pass, info)
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,11 @@ for i in 1 .. count-1:
|
||||||
echo(surface.WriteToPNG(outFile))
|
echo(surface.WriteToPNG(outFile))
|
||||||
surface.Destroy()
|
surface.Destroy()
|
||||||
|
|
||||||
|
type TFoo = object
|
||||||
|
|
||||||
|
converter toPtr*(some: var TFoo): ptr TFoo = (addr some)
|
||||||
|
|
||||||
|
|
||||||
|
proc zoot(x: ptr TFoo) = nil
|
||||||
|
var x: Tfoo
|
||||||
|
zoot(x)
|
||||||
|
|
|
||||||
26
tests/manyloc/keineschweine/README.md
Normal file
26
tests/manyloc/keineschweine/README.md
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
keineSchweine
|
||||||
|
========================
|
||||||
|
Just a dumb little game
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
* Nimrod 0.8.15, Until this version is released I'm working off Nimrod HEAD: https://github.com/Araq/Nimrod
|
||||||
|
* SFML 2.0 (git), https://github.com/LaurentGomila/SFML
|
||||||
|
* CSFML 2.0 (git), https://github.com/LaurentGomila/CSFML
|
||||||
|
* Chipmunk 6.1.1 http://chipmunk-physics.net/downloads.php
|
||||||
|
|
||||||
|
### How to build?
|
||||||
|
|
||||||
|
* `git clone --recursive git://github.com/fowlmouth/keineSchweine.git somedir`
|
||||||
|
* `cd somedir`
|
||||||
|
* `nimrod c -r nakefile test` or `nimrod c -r keineschweine && ./keineschweine`
|
||||||
|
|
||||||
|
### Download the game data
|
||||||
|
|
||||||
|
You need to download the game data before you can play:
|
||||||
|
http://dl.dropbox.com/u/37533467/data-08-01-2012.7z
|
||||||
|
|
||||||
|
Unpack it to the root directory. You can use the nakefile to do this easily:
|
||||||
|
|
||||||
|
* `nimrod c -r nakefile`
|
||||||
|
* `./nakefile download`
|
||||||
21
tests/manyloc/keineschweine/TODO.md
Normal file
21
tests/manyloc/keineschweine/TODO.md
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
##Main State
|
||||||
|
* Add GUI:
|
||||||
|
* Player list
|
||||||
|
* Chat box
|
||||||
|
* Escape menu
|
||||||
|
|
||||||
|
##Lobby
|
||||||
|
* Add GUI:
|
||||||
|
* options menu
|
||||||
|
* key configuration
|
||||||
|
|
||||||
|
## Animations
|
||||||
|
* Need one-shot explosion animations
|
||||||
|
* Thrusters (maybe a particle system instead)
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
* zone server should verify users through the dir server or handle its own users
|
||||||
|
* directory server should handle asset patching
|
||||||
|
|
||||||
|
## Genpacket
|
||||||
|
* add support for branching types with case
|
||||||
10
tests/manyloc/keineschweine/client_settings.json
Normal file
10
tests/manyloc/keineschweine/client_settings.json
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"resolution": [800,600,32],
|
||||||
|
"default-file": "alphazone.json",
|
||||||
|
"alias": "foo",
|
||||||
|
"directory-server":{
|
||||||
|
"host":"localhost",
|
||||||
|
"port":8024
|
||||||
|
},
|
||||||
|
"website":"https://github.com/fowlmouth/keineSchweine"
|
||||||
|
}
|
||||||
1516
tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
Normal file
1516
tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim
Normal file
File diff suppressed because it is too large
Load diff
614
tests/manyloc/keineschweine/dependencies/enet/enet.nim
Normal file
614
tests/manyloc/keineschweine/dependencies/enet/enet.nim
Normal file
|
|
@ -0,0 +1,614 @@
|
||||||
|
discard """Copyright (c) 2002-2012 Lee Salzman
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
"""
|
||||||
|
when defined(Linux):
|
||||||
|
const Lib = "libenet.so.1(|.0.3)"
|
||||||
|
else:
|
||||||
|
{.error: "Your platform has not been accounted for."}
|
||||||
|
{.deadCodeElim: ON.}
|
||||||
|
const
|
||||||
|
ENET_VERSION_MAJOR* = 1
|
||||||
|
ENET_VERSION_MINOR* = 3
|
||||||
|
ENET_VERSION_PATCH* = 3
|
||||||
|
template ENET_VERSION_CREATE(major, minor, patch: expr): expr =
|
||||||
|
(((major) shl 16) or ((minor) shl 8) or (patch))
|
||||||
|
|
||||||
|
const
|
||||||
|
ENET_VERSION* = ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR,
|
||||||
|
ENET_VERSION_PATCH)
|
||||||
|
type
|
||||||
|
TVersion* = cuint
|
||||||
|
TSocketType*{.size: sizeof(cint).} = enum
|
||||||
|
ENET_SOCKET_TYPE_STREAM = 1, ENET_SOCKET_TYPE_DATAGRAM = 2
|
||||||
|
TSocketWait*{.size: sizeof(cint).} = enum
|
||||||
|
ENET_SOCKET_WAIT_NONE = 0, ENET_SOCKET_WAIT_SEND = (1 shl 0),
|
||||||
|
ENET_SOCKET_WAIT_RECEIVE = (1 shl 1)
|
||||||
|
TSocketOption*{.size: sizeof(cint).} = enum
|
||||||
|
ENET_SOCKOPT_NONBLOCK = 1, ENET_SOCKOPT_BROADCAST = 2,
|
||||||
|
ENET_SOCKOPT_RCVBUF = 3, ENET_SOCKOPT_SNDBUF = 4,
|
||||||
|
ENET_SOCKOPT_REUSEADDR = 5
|
||||||
|
const
|
||||||
|
ENET_HOST_ANY* = 0
|
||||||
|
ENET_HOST_BROADCAST* = 0xFFFFFFFF
|
||||||
|
ENET_PORT_ANY* = 0
|
||||||
|
|
||||||
|
ENET_PROTOCOL_MINIMUM_MTU* = 576
|
||||||
|
ENET_PROTOCOL_MAXIMUM_MTU* = 4096
|
||||||
|
ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS* = 32
|
||||||
|
ENET_PROTOCOL_MINIMUM_WINDOW_SIZE* = 4096
|
||||||
|
ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE* = 32768
|
||||||
|
ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT* = 1
|
||||||
|
ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT* = 255
|
||||||
|
ENET_PROTOCOL_MAXIMUM_PEER_ID* = 0x00000FFF
|
||||||
|
type
|
||||||
|
PAddress* = ptr TAddress
|
||||||
|
TAddress*{.pure, final.} = object
|
||||||
|
host*: cuint
|
||||||
|
port*: cushort
|
||||||
|
|
||||||
|
TPacketFlag*{.size: sizeof(cint).} = enum
|
||||||
|
FlagReliable = (1 shl 0),
|
||||||
|
FlagUnsequenced = (1 shl 1),
|
||||||
|
NoAllocate = (1 shl 2),
|
||||||
|
UnreliableFragment = (1 shl 3)
|
||||||
|
|
||||||
|
TENetListNode*{.pure, final.} = object
|
||||||
|
next*: ptr T_ENetListNode
|
||||||
|
previous*: ptr T_ENetListNode
|
||||||
|
|
||||||
|
PENetListIterator* = ptr TENetListNode
|
||||||
|
TENetList*{.pure, final.} = object
|
||||||
|
sentinel*: TENetListNode
|
||||||
|
|
||||||
|
T_ENetPacket*{.pure, final.} = object
|
||||||
|
TPacketFreeCallback* = proc (a2: ptr T_ENetPacket){.cdecl.}
|
||||||
|
|
||||||
|
PPacket* = ptr TPacket
|
||||||
|
TPacket*{.pure, final.} = object
|
||||||
|
referenceCount: csize
|
||||||
|
flags*: cint
|
||||||
|
data*: cstring#ptr cuchar
|
||||||
|
dataLength*: csize
|
||||||
|
freeCallback*: TPacketFreeCallback
|
||||||
|
|
||||||
|
PAcknowledgement* = ptr TAcknowledgement
|
||||||
|
TAcknowledgement*{.pure, final.} = object
|
||||||
|
acknowledgementList*: TEnetListNode
|
||||||
|
sentTime*: cuint
|
||||||
|
command*: TEnetProtocol
|
||||||
|
|
||||||
|
POutgoingCommand* = ptr TOutgoingCommand
|
||||||
|
TOutgoingCommand*{.pure, final.} = object
|
||||||
|
outgoingCommandList*: TEnetListNode
|
||||||
|
reliableSequenceNumber*: cushort
|
||||||
|
unreliableSequenceNumber*: cushort
|
||||||
|
sentTime*: cuint
|
||||||
|
roundTripTimeout*: cuint
|
||||||
|
roundTripTimeoutLimit*: cuint
|
||||||
|
fragmentOffset*: cuint
|
||||||
|
fragmentLength*: cushort
|
||||||
|
sendAttempts*: cushort
|
||||||
|
command*: TEnetProtocol
|
||||||
|
packet*: PPacket
|
||||||
|
|
||||||
|
PIncomingCommand* = ptr TIncomingCommand
|
||||||
|
TIncomingCommand*{.pure, final.} = object
|
||||||
|
incomingCommandList*: TEnetListNode
|
||||||
|
reliableSequenceNumber*: cushort
|
||||||
|
unreliableSequenceNumber*: cushort
|
||||||
|
command*: TEnetProtocol
|
||||||
|
fragmentCount*: cuint
|
||||||
|
fragmentsRemaining*: cuint
|
||||||
|
fragments*: ptr cuint
|
||||||
|
packet*: ptr TPacket
|
||||||
|
|
||||||
|
TPeerState*{.size: sizeof(cint).} = enum
|
||||||
|
ENET_PEER_STATE_DISCONNECTED = 0, ENET_PEER_STATE_CONNECTING = 1,
|
||||||
|
ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2,
|
||||||
|
ENET_PEER_STATE_CONNECTION_PENDING = 3,
|
||||||
|
ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4, ENET_PEER_STATE_CONNECTED = 5,
|
||||||
|
ENET_PEER_STATE_DISCONNECT_LATER = 6, ENET_PEER_STATE_DISCONNECTING = 7,
|
||||||
|
ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8, ENET_PEER_STATE_ZOMBIE = 9
|
||||||
|
|
||||||
|
TENetProtocolCommand*{.size: sizeof(cint).} = enum
|
||||||
|
ENET_PROTOCOL_COMMAND_NONE = 0, ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1,
|
||||||
|
ENET_PROTOCOL_COMMAND_CONNECT = 2,
|
||||||
|
ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3,
|
||||||
|
ENET_PROTOCOL_COMMAND_DISCONNECT = 4, ENET_PROTOCOL_COMMAND_PING = 5,
|
||||||
|
ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6,
|
||||||
|
ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7,
|
||||||
|
ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8,
|
||||||
|
ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9,
|
||||||
|
ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10,
|
||||||
|
ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11,
|
||||||
|
ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12,
|
||||||
|
ENET_PROTOCOL_COMMAND_COUNT = 13, ENET_PROTOCOL_COMMAND_MASK = 0x0000000F
|
||||||
|
TENetProtocolFlag*{.size: sizeof(cint).} = enum
|
||||||
|
ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12,
|
||||||
|
ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 shl 6),
|
||||||
|
ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 shl 7),
|
||||||
|
ENET_PROTOCOL_HEADER_SESSION_MASK = (3 shl 12),
|
||||||
|
ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 shl 14),
|
||||||
|
ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 shl 15),
|
||||||
|
ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED.cint or
|
||||||
|
ENET_PROTOCOL_HEADER_FLAG_SENT_TIME.cint
|
||||||
|
|
||||||
|
TENetProtocolHeader*{.pure, final.} = object
|
||||||
|
peerID*: cushort
|
||||||
|
sentTime*: cushort
|
||||||
|
|
||||||
|
TENetProtocolCommandHeader*{.pure, final.} = object
|
||||||
|
command*: cuchar
|
||||||
|
channelID*: cuchar
|
||||||
|
reliableSequenceNumber*: cushort
|
||||||
|
|
||||||
|
TENetProtocolAcknowledge*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
receivedReliableSequenceNumber*: cushort
|
||||||
|
receivedSentTime*: cushort
|
||||||
|
|
||||||
|
TENetProtocolConnect*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
outgoingPeerID*: cushort
|
||||||
|
incomingSessionID*: cuchar
|
||||||
|
outgoingSessionID*: cuchar
|
||||||
|
mtu*: cuint
|
||||||
|
windowSize*: cuint
|
||||||
|
channelCount*: cuint
|
||||||
|
incomingBandwidth*: cuint
|
||||||
|
outgoingBandwidth*: cuint
|
||||||
|
packetThrottleInterval*: cuint
|
||||||
|
packetThrottleAcceleration*: cuint
|
||||||
|
packetThrottleDeceleration*: cuint
|
||||||
|
connectID*: cuint
|
||||||
|
data*: cuint
|
||||||
|
|
||||||
|
TENetProtocolVerifyConnect*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
outgoingPeerID*: cushort
|
||||||
|
incomingSessionID*: cuchar
|
||||||
|
outgoingSessionID*: cuchar
|
||||||
|
mtu*: cuint
|
||||||
|
windowSize*: cuint
|
||||||
|
channelCount*: cuint
|
||||||
|
incomingBandwidth*: cuint
|
||||||
|
outgoingBandwidth*: cuint
|
||||||
|
packetThrottleInterval*: cuint
|
||||||
|
packetThrottleAcceleration*: cuint
|
||||||
|
packetThrottleDeceleration*: cuint
|
||||||
|
connectID*: cuint
|
||||||
|
|
||||||
|
TENetProtocolBandwidthLimit*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
incomingBandwidth*: cuint
|
||||||
|
outgoingBandwidth*: cuint
|
||||||
|
|
||||||
|
TENetProtocolThrottleConfigure*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
packetThrottleInterval*: cuint
|
||||||
|
packetThrottleAcceleration*: cuint
|
||||||
|
packetThrottleDeceleration*: cuint
|
||||||
|
|
||||||
|
TENetProtocolDisconnect*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
data*: cuint
|
||||||
|
|
||||||
|
TENetProtocolPing*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
|
||||||
|
TENetProtocolSendReliable*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
dataLength*: cushort
|
||||||
|
|
||||||
|
TENetProtocolSendUnreliable*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
unreliableSequenceNumber*: cushort
|
||||||
|
dataLength*: cushort
|
||||||
|
|
||||||
|
TENetProtocolSendUnsequenced*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
unsequencedGroup*: cushort
|
||||||
|
dataLength*: cushort
|
||||||
|
|
||||||
|
TENetProtocolSendFragment*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
startSequenceNumber*: cushort
|
||||||
|
dataLength*: cushort
|
||||||
|
fragmentCount*: cuint
|
||||||
|
fragmentNumber*: cuint
|
||||||
|
totalLength*: cuint
|
||||||
|
fragmentOffset*: cuint
|
||||||
|
|
||||||
|
## this is incomplete; need helper templates or something
|
||||||
|
## ENetProtocol
|
||||||
|
TENetProtocol*{.pure, final.} = object
|
||||||
|
header*: TENetProtocolCommandHeader
|
||||||
|
const
|
||||||
|
ENET_BUFFER_MAXIMUM* = (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS)
|
||||||
|
ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024
|
||||||
|
ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024
|
||||||
|
ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000
|
||||||
|
ENET_HOST_DEFAULT_MTU = 1400
|
||||||
|
|
||||||
|
ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500
|
||||||
|
ENET_PEER_DEFAULT_PACKET_THROTTLE = 32
|
||||||
|
ENET_PEER_PACKET_THROTTLE_SCALE = 32
|
||||||
|
ENET_PEER_PACKET_THROTTLE_COUNTER = 7
|
||||||
|
ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2
|
||||||
|
ENET_PEER_PACKET_THROTTLE_DECELERATION = 2
|
||||||
|
ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000
|
||||||
|
ENET_PEER_PACKET_LOSS_SCALE = (1 shl 16)
|
||||||
|
ENET_PEER_PACKET_LOSS_INTERVAL = 10000
|
||||||
|
ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024
|
||||||
|
ENET_PEER_TIMEOUT_LIMIT = 32
|
||||||
|
ENET_PEER_TIMEOUT_MINIMUM = 5000
|
||||||
|
ENET_PEER_TIMEOUT_MAXIMUM = 30000
|
||||||
|
ENET_PEER_PING_INTERVAL = 500
|
||||||
|
ENET_PEER_UNSEQUENCED_WINDOWS = 64
|
||||||
|
ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024
|
||||||
|
ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32
|
||||||
|
ENET_PEER_RELIABLE_WINDOWS = 16
|
||||||
|
ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000
|
||||||
|
ENET_PEER_FREE_RELIABLE_WINDOWS = 8
|
||||||
|
|
||||||
|
when defined(Linux):
|
||||||
|
import posix
|
||||||
|
const
|
||||||
|
ENET_SOCKET_NULL*: cint = -1
|
||||||
|
type
|
||||||
|
TENetSocket* = cint
|
||||||
|
PEnetBuffer* = ptr object
|
||||||
|
TENetBuffer*{.pure, final.} = object
|
||||||
|
data*: pointer
|
||||||
|
dataLength*: csize
|
||||||
|
TENetSocketSet* = Tfd_set
|
||||||
|
## see if these are different on win32, if not then get rid of these
|
||||||
|
template ENET_HOST_TO_NET_16*(value: expr): expr =
|
||||||
|
(htons(value))
|
||||||
|
template ENET_HOST_TO_NET_32*(value: expr): expr =
|
||||||
|
(htonl(value))
|
||||||
|
template ENET_NET_TO_HOST_16*(value: expr): expr =
|
||||||
|
(ntohs(value))
|
||||||
|
template ENET_NET_TO_HOST_32*(value: expr): expr =
|
||||||
|
(ntohl(value))
|
||||||
|
|
||||||
|
template ENET_SOCKETSET_EMPTY*(sockset: expr): expr =
|
||||||
|
FD_ZERO(addr((sockset)))
|
||||||
|
template ENET_SOCKETSET_ADD*(sockset, socket: expr): expr =
|
||||||
|
FD_SET(socket, addr((sockset)))
|
||||||
|
template ENET_SOCKETSET_REMOVE*(sockset, socket: expr): expr =
|
||||||
|
FD_CLEAR(socket, addr((sockset)))
|
||||||
|
template ENET_SOCKETSET_CHECK*(sockset, socket: expr): expr =
|
||||||
|
FD_ISSET(socket, addr((sockset)))
|
||||||
|
|
||||||
|
when defined(Windows):
|
||||||
|
## put the content of win32.h in here
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
PChannel* = ptr TChannel
|
||||||
|
TChannel*{.pure, final.} = object
|
||||||
|
outgoingReliableSequenceNumber*: cushort
|
||||||
|
outgoingUnreliableSequenceNumber*: cushort
|
||||||
|
usedReliableWindows*: cushort
|
||||||
|
reliableWindows*: array[0..ENET_PEER_RELIABLE_WINDOWS - 1, cushort]
|
||||||
|
incomingReliableSequenceNumber*: cushort
|
||||||
|
incomingUnreliableSequenceNumber*: cushort
|
||||||
|
incomingReliableCommands*: TENetList
|
||||||
|
incomingUnreliableCommands*: TENetList
|
||||||
|
|
||||||
|
PPeer* = ptr TPeer
|
||||||
|
TPeer*{.pure, final.} = object
|
||||||
|
dispatchList*: TEnetListNode
|
||||||
|
host*: ptr THost
|
||||||
|
outgoingPeerID*: cushort
|
||||||
|
incomingPeerID*: cushort
|
||||||
|
connectID*: cuint
|
||||||
|
outgoingSessionID*: cuchar
|
||||||
|
incomingSessionID*: cuchar
|
||||||
|
address*: TAddress
|
||||||
|
data*: pointer
|
||||||
|
state*: TPeerState
|
||||||
|
channels*: PChannel
|
||||||
|
channelCount*: csize
|
||||||
|
incomingBandwidth*: cuint
|
||||||
|
outgoingBandwidth*: cuint
|
||||||
|
incomingBandwidthThrottleEpoch*: cuint
|
||||||
|
outgoingBandwidthThrottleEpoch*: cuint
|
||||||
|
incomingDataTotal*: cuint
|
||||||
|
outgoingDataTotal*: cuint
|
||||||
|
lastSendTime*: cuint
|
||||||
|
lastReceiveTime*: cuint
|
||||||
|
nextTimeout*: cuint
|
||||||
|
earliestTimeout*: cuint
|
||||||
|
packetLossEpoch*: cuint
|
||||||
|
packetsSent*: cuint
|
||||||
|
packetsLost*: cuint
|
||||||
|
packetLoss*: cuint
|
||||||
|
packetLossVariance*: cuint
|
||||||
|
packetThrottle*: cuint
|
||||||
|
packetThrottleLimit*: cuint
|
||||||
|
packetThrottleCounter*: cuint
|
||||||
|
packetThrottleEpoch*: cuint
|
||||||
|
packetThrottleAcceleration*: cuint
|
||||||
|
packetThrottleDeceleration*: cuint
|
||||||
|
packetThrottleInterval*: cuint
|
||||||
|
lastRoundTripTime*: cuint
|
||||||
|
lowestRoundTripTime*: cuint
|
||||||
|
lastRoundTripTimeVariance*: cuint
|
||||||
|
highestRoundTripTimeVariance*: cuint
|
||||||
|
roundTripTime*: cuint
|
||||||
|
roundTripTimeVariance*: cuint
|
||||||
|
mtu*: cuint
|
||||||
|
windowSize*: cuint
|
||||||
|
reliableDataInTransit*: cuint
|
||||||
|
outgoingReliableSequenceNumber*: cushort
|
||||||
|
acknowledgements*: TENetList
|
||||||
|
sentReliableCommands*: TENetList
|
||||||
|
sentUnreliableCommands*: TENetList
|
||||||
|
outgoingReliableCommands*: TENetList
|
||||||
|
outgoingUnreliableCommands*: TENetList
|
||||||
|
dispatchedCommands*: TENetList
|
||||||
|
needsDispatch*: cint
|
||||||
|
incomingUnsequencedGroup*: cushort
|
||||||
|
outgoingUnsequencedGroup*: cushort
|
||||||
|
unsequencedWindow*: array[0..ENET_PEER_UNSEQUENCED_WINDOW_SIZE div 32 - 1,
|
||||||
|
cuint]
|
||||||
|
eventData*: cuint
|
||||||
|
|
||||||
|
PCompressor* = ptr TCompressor
|
||||||
|
TCompressor*{.pure, final.} = object
|
||||||
|
context*: pointer
|
||||||
|
compress*: proc (context: pointer; inBuffers: ptr TEnetBuffer;
|
||||||
|
inBufferCount: csize; inLimit: csize;
|
||||||
|
outData: ptr cuchar; outLimit: csize): csize{.cdecl.}
|
||||||
|
decompress*: proc (context: pointer; inData: ptr cuchar; inLimit: csize;
|
||||||
|
outData: ptr cuchar; outLimit: csize): csize{.cdecl.}
|
||||||
|
destroy*: proc (context: pointer){.cdecl.}
|
||||||
|
|
||||||
|
TChecksumCallback* = proc (buffers: ptr TEnetBuffer; bufferCount: csize): cuint{.
|
||||||
|
cdecl.}
|
||||||
|
|
||||||
|
PHost* = ptr THost
|
||||||
|
THost*{.pure, final.} = object
|
||||||
|
socket*: TEnetSocket
|
||||||
|
address*: TAddress
|
||||||
|
incomingBandwidth*: cuint
|
||||||
|
outgoingBandwidth*: cuint
|
||||||
|
bandwidthThrottleEpoch*: cuint
|
||||||
|
mtu*: cuint
|
||||||
|
randomSeed*: cuint
|
||||||
|
recalculateBandwidthLimits*: cint
|
||||||
|
peers*: ptr TPeer
|
||||||
|
peerCount*: csize
|
||||||
|
channelLimit*: csize
|
||||||
|
serviceTime*: cuint
|
||||||
|
dispatchQueue*: TEnetList
|
||||||
|
continueSending*: cint
|
||||||
|
packetSize*: csize
|
||||||
|
headerFlags*: cushort
|
||||||
|
commands*: array[0..ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS - 1,
|
||||||
|
TEnetProtocol]
|
||||||
|
commandCount*: csize
|
||||||
|
buffers*: array[0..ENET_BUFFER_MAXIMUM - 1, TEnetBuffer]
|
||||||
|
bufferCount*: csize
|
||||||
|
checksum*: TChecksumCallback
|
||||||
|
compressor*: TCompressor
|
||||||
|
packetData*: array[0..ENET_PROTOCOL_MAXIMUM_MTU - 1,
|
||||||
|
array[0..2 - 1, cuchar]]
|
||||||
|
receivedAddress*: TAddress
|
||||||
|
receivedData*: ptr cuchar
|
||||||
|
receivedDataLength*: csize
|
||||||
|
totalSentData*: cuint
|
||||||
|
totalSentPackets*: cuint
|
||||||
|
totalReceivedData*: cuint
|
||||||
|
totalReceivedPackets*: cuint
|
||||||
|
|
||||||
|
TEventType*{.size: sizeof(cint).} = enum
|
||||||
|
EvtNone = 0, EvtConnect = 1,
|
||||||
|
EvtDisconnect = 2, EvtReceive = 3
|
||||||
|
PEvent* = ptr TEvent
|
||||||
|
TEvent*{.pure, final.} = object
|
||||||
|
kind*: TEventType
|
||||||
|
peer*: ptr TPeer
|
||||||
|
channelID*: cuchar
|
||||||
|
data*: cuint
|
||||||
|
packet*: ptr TPacket
|
||||||
|
|
||||||
|
TENetCallbacks*{.pure, final.} = object
|
||||||
|
malloc*: proc (size: csize): pointer{.cdecl.}
|
||||||
|
free*: proc (memory: pointer){.cdecl.}
|
||||||
|
no_memory*: proc (){.cdecl.}
|
||||||
|
|
||||||
|
{.push callConv:cdecl.}
|
||||||
|
proc enet_malloc*(a2: csize): pointer{.
|
||||||
|
importc: "enet_malloc", dynlib: Lib.}
|
||||||
|
proc enet_free*(a2: pointer){.
|
||||||
|
importc: "enet_free", dynlib: Lib.}
|
||||||
|
|
||||||
|
proc enetInit*(): cint{.
|
||||||
|
importc: "enet_initialize", dynlib: Lib.}
|
||||||
|
proc enetInit*(version: TVersion; inits: ptr TENetCallbacks): cint{.
|
||||||
|
importc: "enet_initialize_with_callbacks", dynlib: Lib.}
|
||||||
|
proc enetDeinit*(){.
|
||||||
|
importc: "enet_deinitialize", dynlib: Lib.}
|
||||||
|
proc enet_time_get*(): cuint{.
|
||||||
|
importc: "enet_time_get", dynlib: Lib.}
|
||||||
|
proc enet_time_set*(a2: cuint){.
|
||||||
|
importc: "enet_time_set", dynlib: Lib.}
|
||||||
|
|
||||||
|
#enet docs are pretty lacking, i'm not sure what the names of these arguments should be
|
||||||
|
proc createSocket*(kind: TSocketType): TEnetSocket{.
|
||||||
|
importc: "enet_socket_create", dynlib: Lib.}
|
||||||
|
proc bindTo*(socket: TEnetSocket; address: var TAddress): cint{.
|
||||||
|
importc: "enet_socket_bind", dynlib: Lib.}
|
||||||
|
proc bindTo*(socket: TEnetSocket; address: ptr TAddress): cint{.
|
||||||
|
importc: "enet_socket_bind", dynlib: Lib.}
|
||||||
|
proc listen*(socket: TEnetSocket; a3: cint): cint{.
|
||||||
|
importc: "enet_socket_listen", dynlib: Lib.}
|
||||||
|
proc accept*(socket: TEnetSocket; address: var TAddress): TEnetSocket{.
|
||||||
|
importc: "enet_socket_accept", dynlib: Lib.}
|
||||||
|
proc accept*(socket: TEnetSocket; address: ptr TAddress): TEnetSocket{.
|
||||||
|
importc: "enet_socket_accept", dynlib: Lib.}
|
||||||
|
proc connect*(socket: TEnetSocket; address: var TAddress): cint{.
|
||||||
|
importc: "enet_socket_connect", dynlib: Lib.}
|
||||||
|
proc connect*(socket: TEnetSocket; address: ptr TAddress): cint{.
|
||||||
|
importc: "enet_socket_connect", dynlib: Lib.}
|
||||||
|
proc send*(socket: TEnetSocket; address: var TAddress; buffer: ptr TEnetBuffer; size: csize): cint{.
|
||||||
|
importc: "enet_socket_send", dynlib: Lib.}
|
||||||
|
proc send*(socket: TEnetSocket; address: ptr TAddress; buffer: ptr TEnetBuffer; size: csize): cint{.
|
||||||
|
importc: "enet_socket_send", dynlib: Lib.}
|
||||||
|
proc receive*(socket: TEnetSocket; address: var TAddress;
|
||||||
|
buffer: ptr TEnetBuffer; size: csize): cint{.
|
||||||
|
importc: "enet_socket_receive", dynlib: Lib.}
|
||||||
|
proc receive*(socket: TEnetSocket; address: ptr TAddress;
|
||||||
|
buffer: ptr TEnetBuffer; size: csize): cint{.
|
||||||
|
importc: "enet_socket_receive", dynlib: Lib.}
|
||||||
|
proc wait*(socket: TEnetSocket; a3: ptr cuint; a4: cuint): cint{.
|
||||||
|
importc: "enet_socket_wait", dynlib: Lib.}
|
||||||
|
proc setOption*(socket: TEnetSocket; a3: TSocketOption; a4: cint): cint{.
|
||||||
|
importc: "enet_socket_set_option", dynlib: Lib.}
|
||||||
|
proc destroy*(socket: TEnetSocket){.
|
||||||
|
importc: "enet_socket_destroy", dynlib: Lib.}
|
||||||
|
proc select*(socket: TEnetSocket; a3: ptr TENetSocketSet;
|
||||||
|
a4: ptr TENetSocketSet; a5: cuint): cint{.
|
||||||
|
importc: "enet_socketset_select", dynlib: Lib.}
|
||||||
|
|
||||||
|
proc setHost*(address: PAddress; hostName: cstring): cint{.
|
||||||
|
importc: "enet_address_set_host", dynlib: Lib.}
|
||||||
|
proc setHost*(address: var TAddress; hostName: cstring): cint{.
|
||||||
|
importc: "enet_address_set_host", dynlib: Lib.}
|
||||||
|
proc getHostIP*(address: var TAddress; hostName: cstring; nameLength: csize): cint{.
|
||||||
|
importc: "enet_address_get_host_ip", dynlib: Lib.}
|
||||||
|
proc getHost*(address: var TAddress; hostName: cstring; nameLength: csize): cint{.
|
||||||
|
importc: "enet_address_get_host", dynlib: Lib.}
|
||||||
|
|
||||||
|
## Call the above two funcs but trim the result string
|
||||||
|
proc getHostIP*(address: var TAddress; hostName: var string; nameLength: csize): cint{.inline.} =
|
||||||
|
hostName.setLen nameLength
|
||||||
|
result = getHostIP(address, cstring(hostName), nameLength)
|
||||||
|
if result == 0:
|
||||||
|
hostName.setLen(len(cstring(hostName)))
|
||||||
|
proc getHost*(address: var TAddress; hostName: var string; nameLength: csize): cint{.inline.} =
|
||||||
|
hostName.setLen nameLength
|
||||||
|
result = getHost(address, cstring(hostName), nameLength)
|
||||||
|
if result == 0:
|
||||||
|
hostName.setLen(len(cstring(hostName)))
|
||||||
|
|
||||||
|
proc createPacket*(data: pointer; len: csize; flag: TPacketFlag): PPacket{.
|
||||||
|
importc: "enet_packet_create", dynlib: Lib.}
|
||||||
|
proc destroy*(packet: PPacket){.
|
||||||
|
importc: "enet_packet_destroy", dynlib: Lib.}
|
||||||
|
proc resize*(packet: PPacket; dataLength: csize): cint{.
|
||||||
|
importc: "enet_packet_resize", dynlib: Lib.}
|
||||||
|
|
||||||
|
proc crc32*(buffers: ptr TEnetBuffer; bufferCount: csize): cuint{.
|
||||||
|
importc: "enet_crc32", dynlib: Lib.}
|
||||||
|
|
||||||
|
proc createHost*(address: ptr TAddress; maxConnections, maxChannels: csize; downSpeed, upSpeed: cuint): PHost{.
|
||||||
|
importc: "enet_host_create", dynlib: Lib.}
|
||||||
|
proc createHost*(address: var TAddress; maxConnections, maxChannels: csize; downSpeed, upSpeed: cuint): PHost{.
|
||||||
|
importc: "enet_host_create", dynlib: Lib.}
|
||||||
|
proc destroy*(host: PHost){.
|
||||||
|
importc: "enet_host_destroy", dynlib: Lib.}
|
||||||
|
proc connect*(host: PHost; address: ptr TAddress; channelCount: csize; data: cuint): PPeer{.
|
||||||
|
importc: "enet_host_connect", dynlib: Lib.}
|
||||||
|
proc connect*(host: PHost; address: var TAddress; channelCount: csize; data: cuint): PPeer{.
|
||||||
|
importc: "enet_host_connect", dynlib: Lib.}
|
||||||
|
|
||||||
|
proc checkEvents*(host: PHost; event: var TEvent): cint{.
|
||||||
|
importc: "enet_host_check_events", dynlib: Lib.}
|
||||||
|
proc checkEvents*(host: PHost; event: ptr TEvent): cint{.
|
||||||
|
importc: "enet_host_check_events", dynlib: Lib.}
|
||||||
|
proc hostService*(host: PHost; event: var TEvent; timeout: cuint): cint{.
|
||||||
|
importc: "enet_host_service", dynlib: Lib.}
|
||||||
|
proc hostService*(host: PHost; event: ptr TEvent; timeout: cuint): cint{.
|
||||||
|
importc: "enet_host_service", dynlib: Lib.}
|
||||||
|
proc flush*(host: PHost){.
|
||||||
|
importc: "enet_host_flush", dynlib: Lib.}
|
||||||
|
proc broadcast*(host: PHost; channelID: cuchar; packet: PPacket){.
|
||||||
|
importc: "enet_host_broadcast", dynlib: Lib.}
|
||||||
|
proc compress*(host: PHost; compressor: PCompressor){.
|
||||||
|
importc: "enet_host_compress", dynlib: Lib.}
|
||||||
|
proc compressWithRangeCoder*(host: PHost): cint{.
|
||||||
|
importc: "enet_host_compress_with_range_coder", dynlib: Lib.}
|
||||||
|
proc channelLimit*(host: PHost; channelLimit: csize){.
|
||||||
|
importc: "enet_host_channel_limit", dynlib: Lib.}
|
||||||
|
proc bandwidthLimit*(host: PHost; incoming, outgoing: cuint){.
|
||||||
|
importc: "enet_host_bandwidth_limit", dynlib: Lib.}
|
||||||
|
proc bandwidthThrottle*(host: PHost){.
|
||||||
|
importc: "enet_host_bandwidth_throttle", dynlib: Lib.}
|
||||||
|
|
||||||
|
|
||||||
|
proc send*(peer: PPeer; channel: cuchar; packet: PPacket): cint{.
|
||||||
|
importc: "enet_peer_send", dynlib: Lib.}
|
||||||
|
proc receive*(peer: PPeer; channelID: ptr cuchar): PPacket{.
|
||||||
|
importc: "enet_peer_receive", dynlib: Lib.}
|
||||||
|
proc ping*(peer: PPeer){.
|
||||||
|
importc: "enet_peer_ping", dynlib: Lib.}
|
||||||
|
proc reset*(peer: PPeer){.
|
||||||
|
importc: "enet_peer_reset", dynlib: Lib.}
|
||||||
|
proc disconnect*(peer: PPeer; a3: cuint){.
|
||||||
|
importc: "enet_peer_disconnect", dynlib: Lib.}
|
||||||
|
proc disconnectNow*(peer: PPeer; a3: cuint){.
|
||||||
|
importc: "enet_peer_disconnect_now", dynlib: Lib.}
|
||||||
|
proc disconnectLater*(peer: PPeer; a3: cuint){.
|
||||||
|
importc: "enet_peer_disconnect_later", dynlib: Lib.}
|
||||||
|
proc throttleConfigure*(peer: PPeer; interval, acceleration, deceleration: cuint){.
|
||||||
|
importc: "enet_peer_throttle_configure", dynlib: Lib.}
|
||||||
|
proc throttle*(peer: PPeer; rtt: cuint): cint{.
|
||||||
|
importc: "enet_peer_throttle", dynlib: Lib.}
|
||||||
|
proc resetQueues*(peer: PPeer){.
|
||||||
|
importc: "enet_peer_reset_queues", dynlib: Lib.}
|
||||||
|
proc setupOutgoingCommand*(peer: PPeer; outgoingCommand: POutgoingCommand){.
|
||||||
|
importc: "enet_peer_setup_outgoing_command", dynlib: Lib.}
|
||||||
|
|
||||||
|
proc queueOutgoingCommand*(peer: PPeer; command: ptr TEnetProtocol;
|
||||||
|
packet: PPacket; offset: cuint; length: cushort): POutgoingCommand{.
|
||||||
|
importc: "enet_peer_queue_outgoing_command", dynlib: Lib.}
|
||||||
|
proc queueIncomingCommand*(peer: PPeer; command: ptr TEnetProtocol;
|
||||||
|
packet: PPacket; fragmentCount: cuint): PIncomingCommand{.
|
||||||
|
importc: "enet_peer_queue_incoming_command", dynlib: Lib.}
|
||||||
|
proc queueAcknowledgement*(peer: PPeer; command: ptr TEnetProtocol;
|
||||||
|
sentTime: cushort): PAcknowledgement{.
|
||||||
|
importc: "enet_peer_queue_acknowledgement", dynlib: Lib.}
|
||||||
|
proc dispatchIncomingUnreliableCommands*(peer: PPeer; channel: PChannel){.
|
||||||
|
importc: "enet_peer_dispatch_incoming_unreliable_commands", dynlib: Lib.}
|
||||||
|
proc dispatchIncomingReliableCommands*(peer: PPeer; channel: PChannel){.
|
||||||
|
importc: "enet_peer_dispatch_incoming_reliable_commands", dynlib: Lib.}
|
||||||
|
|
||||||
|
proc createRangeCoder*(): pointer{.
|
||||||
|
importc: "enet_range_coder_create", dynlib: Lib.}
|
||||||
|
proc rangeCoderDestroy*(context: pointer){.
|
||||||
|
importc: "enet_range_coder_destroy", dynlib: Lib.}
|
||||||
|
proc rangeCoderCompress*(context: pointer; inBuffers: PEnetBuffer; inLimit,
|
||||||
|
bufferCount: csize; outData: cstring; outLimit: csize): csize{.
|
||||||
|
importc: "enet_range_coder_compress", dynlib: Lib.}
|
||||||
|
proc rangeCoderDecompress*(context: pointer; inData: cstring; inLimit: csize;
|
||||||
|
outData: cstring; outLimit: csize): csize{.
|
||||||
|
importc: "enet_range_coder_decompress", dynlib: Lib.}
|
||||||
|
proc protocolCommandSize*(commandNumber: cuchar): csize{.
|
||||||
|
importc: "enet_protocol_command_size", dynlib: Lib.}
|
||||||
|
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
|
from hashes import `!$`, `!&`, THash, hash
|
||||||
|
proc hash*(x: TAddress): THash {.nimcall, noSideEffect.} =
|
||||||
|
result = !$(hash(x.host.int32) !& hash(x.port.int16))
|
||||||
|
|
||||||
49
tests/manyloc/keineschweine/dependencies/enet/testclient.nim
Normal file
49
tests/manyloc/keineschweine/dependencies/enet/testclient.nim
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
import enet, strutils
|
||||||
|
|
||||||
|
if enetInit() != 0:
|
||||||
|
quit "Could not initialize ENet"
|
||||||
|
var
|
||||||
|
address: enet.TAddress
|
||||||
|
event: TEvent
|
||||||
|
peer: PPeer
|
||||||
|
client: PHost
|
||||||
|
|
||||||
|
client = createHost(nil, 1, 2, 0, 0)
|
||||||
|
if client == nil:
|
||||||
|
quit "Could not create client!"
|
||||||
|
|
||||||
|
if setHost(addr address, "localhost") != 0:
|
||||||
|
quit "Could not set host"
|
||||||
|
address.port = 8024
|
||||||
|
|
||||||
|
peer = client.connect(addr address, 2, 0)
|
||||||
|
if peer == nil:
|
||||||
|
quit "No available peers"
|
||||||
|
|
||||||
|
block:
|
||||||
|
var bConnected = false
|
||||||
|
while not bConnected:
|
||||||
|
if client.hostService(event, 5000) > 0 and event.kind == EvtConnect:
|
||||||
|
echo "Connected"
|
||||||
|
bConnected = true
|
||||||
|
else:
|
||||||
|
echo "Connection failed"
|
||||||
|
quit 0
|
||||||
|
|
||||||
|
var runServer = true
|
||||||
|
while client.hostService(event, 1000) >= 0 and runServer:
|
||||||
|
case event.kind
|
||||||
|
of EvtReceive:
|
||||||
|
echo "Recvd ($1) $2 ".format(
|
||||||
|
event.packet.dataLength,
|
||||||
|
event.packet.data)
|
||||||
|
of EvtDisconnect:
|
||||||
|
echo "Disconnected"
|
||||||
|
event.peer.data = nil
|
||||||
|
runServer = false
|
||||||
|
of EvtNone: discard
|
||||||
|
else:
|
||||||
|
echo repr(event)
|
||||||
|
|
||||||
|
|
||||||
|
client.destroy()
|
||||||
45
tests/manyloc/keineschweine/dependencies/enet/testserver.nim
Normal file
45
tests/manyloc/keineschweine/dependencies/enet/testserver.nim
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
import enet, strutils
|
||||||
|
var
|
||||||
|
address: enet.TAddress
|
||||||
|
server: PHost
|
||||||
|
event: TEvent
|
||||||
|
|
||||||
|
if enetInit() != 0:
|
||||||
|
quit "Could not initialize ENet"
|
||||||
|
|
||||||
|
address.host = EnetHostAny
|
||||||
|
address.port = 8024
|
||||||
|
|
||||||
|
server = enet.createHost(
|
||||||
|
addr address, 32, 2, 0, 0)
|
||||||
|
if server == nil:
|
||||||
|
quit "Could not create the server!"
|
||||||
|
|
||||||
|
while server.hostService(addr event, 2500) >= 0:
|
||||||
|
case event.kind
|
||||||
|
of EvtConnect:
|
||||||
|
echo "New client from $1:$2".format(event.peer.address.host, event.peer.address.port)
|
||||||
|
|
||||||
|
var
|
||||||
|
msg = "hello"
|
||||||
|
resp = createPacket(cstring(msg), msg.len + 1, FlagReliable)
|
||||||
|
|
||||||
|
if event.peer.send(0.cuchar, resp) < 0:
|
||||||
|
echo "FAILED"
|
||||||
|
else:
|
||||||
|
echo "Replied"
|
||||||
|
of EvtReceive:
|
||||||
|
echo "Recvd ($1) $2 ".format(
|
||||||
|
event.packet.dataLength,
|
||||||
|
event.packet.data)
|
||||||
|
|
||||||
|
destroy(event.packet)
|
||||||
|
|
||||||
|
of EvtDisconnect:
|
||||||
|
echo "Disconnected"
|
||||||
|
event.peer.data = nil
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
server.destroy()
|
||||||
|
enetDeinit()
|
||||||
295
tests/manyloc/keineschweine/dependencies/genpacket/genpacket.nim
Normal file
295
tests/manyloc/keineschweine/dependencies/genpacket/genpacket.nim
Normal file
|
|
@ -0,0 +1,295 @@
|
||||||
|
import macros, macro_dsl, streams, streams_enh
|
||||||
|
from strutils import format
|
||||||
|
|
||||||
|
template newLenName(): stmt {.immediate.} =
|
||||||
|
let lenName {.inject.} = ^("len"& $lenNames)
|
||||||
|
inc(lenNames)
|
||||||
|
|
||||||
|
template defPacketImports*(): stmt {.immediate, dirty.} =
|
||||||
|
import macros, macro_dsl, streams, streams_enh
|
||||||
|
from strutils import format
|
||||||
|
|
||||||
|
proc `$`*[T](x: seq[T]): string =
|
||||||
|
result = "[seq len="
|
||||||
|
result.add($x.len)
|
||||||
|
result.add ':'
|
||||||
|
for i in 0.. <len(x):
|
||||||
|
result.add " "
|
||||||
|
result.add($x[i])
|
||||||
|
result.add ']'
|
||||||
|
|
||||||
|
macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
|
||||||
|
result = newNimNode(nnkStmtList)
|
||||||
|
let
|
||||||
|
typeName = quoted2ident(typeNameN)
|
||||||
|
packetID = ^"p"
|
||||||
|
streamID = ^"s"
|
||||||
|
var
|
||||||
|
constructorParams = newNimNode(nnkFormalParams).und(typeName)
|
||||||
|
constructor = newNimNode(nnkProcDef).und(
|
||||||
|
postfix(^("new"& $typeName.ident), "*"),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
constructorParams,
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode())
|
||||||
|
pack = newNimNode(nnkProcDef).und(
|
||||||
|
postfix(^"pack", "*"),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkFormalParams).und(
|
||||||
|
emptyNode(), # : void
|
||||||
|
newNimNode(nnkIdentDefs).und(
|
||||||
|
packetID, # p: var typeName
|
||||||
|
newNimNode(nnkVarTy).und(typeName),
|
||||||
|
emptyNode()),
|
||||||
|
newNimNode(nnkIdentDefs).und(
|
||||||
|
streamID, # s: PStream
|
||||||
|
^"PStream",
|
||||||
|
newNimNode(nnkNilLit))),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode())
|
||||||
|
read = newNimNode(nnkProcDef).und(
|
||||||
|
newIdentNode("read"& $typeName.ident).postfix("*"),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkFormalParams).und(
|
||||||
|
typeName, #result type
|
||||||
|
newNimNode(nnkIdentDefs).und(
|
||||||
|
streamID, # s: PStream = nil
|
||||||
|
^"PStream",
|
||||||
|
newNimNode(nnkNilLit))),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode())
|
||||||
|
constructorBody = newNimNode(nnkStmtList)
|
||||||
|
packBody = newNimNode(nnkStmtList)
|
||||||
|
readBody = newNimNode(nnkStmtList)
|
||||||
|
lenNames = 0
|
||||||
|
for i in 0.. typeFields.len - 1:
|
||||||
|
let
|
||||||
|
name = typeFields[i][0]
|
||||||
|
dotName = packetID.dot(name)
|
||||||
|
resName = newIdentNode(!"result").dot(name)
|
||||||
|
case typeFields[i][1].kind
|
||||||
|
of nnkBracketExpr: #ex: paddedstring[32, '\0'], array[range, type]
|
||||||
|
case $typeFields[i][1][0].ident
|
||||||
|
of "paddedstring":
|
||||||
|
let length = typeFields[i][1][1]
|
||||||
|
let padChar = typeFields[i][1][2]
|
||||||
|
packBody.add(newCall(
|
||||||
|
"writePaddedStr", streamID, dotName, length, padChar))
|
||||||
|
## result.name = readPaddedStr(s, length, char)
|
||||||
|
readBody.add(resName := newCall(
|
||||||
|
"readPaddedStr", streamID, length, padChar))
|
||||||
|
## make the type a string
|
||||||
|
typeFields[i] = newNimNode(nnkIdentDefs).und(
|
||||||
|
name,
|
||||||
|
^"string",
|
||||||
|
newNimNode(nnkEmpty))
|
||||||
|
of "array":
|
||||||
|
readBody.add(
|
||||||
|
newNimNode(nnkDiscardStmt).und(
|
||||||
|
newCall("readData", streamID, newNimNode(nnkAddr).und(resName), newCall("sizeof", resName))))
|
||||||
|
packBody.add(
|
||||||
|
newCall("writeData", streamID, newNimNode(nnkAddr).und(dotName), newCall("sizeof", dotName)))
|
||||||
|
of "seq":
|
||||||
|
## let lenX = readInt16(s)
|
||||||
|
newLenName()
|
||||||
|
let
|
||||||
|
item = ^"item" ## item name in our iterators
|
||||||
|
seqType = typeFields[i][1][1] ## type of seq
|
||||||
|
readName = newIdentNode("read"& $seqType.ident)
|
||||||
|
readBody.add(newNimNode(nnkLetSection).und(
|
||||||
|
newNimNode(nnkIdentDefs).und(
|
||||||
|
lenName,
|
||||||
|
newNimNode(nnkEmpty),
|
||||||
|
newCall("readInt16", streamID))))
|
||||||
|
readBody.add( ## result.name = @[]
|
||||||
|
resName := ("@".prefix(newNimNode(nnkBracket))),
|
||||||
|
newNimNode(nnkForStmt).und( ## for item in 1..len:
|
||||||
|
item,
|
||||||
|
infix(1.lit, "..", lenName),
|
||||||
|
newNimNode(nnkStmtList).und(
|
||||||
|
newCall( ## add(result.name, unpack[seqType](stream))
|
||||||
|
"add", resName, newNimNode(nnkCall).und(readName, streamID)
|
||||||
|
) ) ) )
|
||||||
|
packbody.add(
|
||||||
|
newNimNode(nnkVarSection).und(newNimNode(nnkIdentDefs).und(
|
||||||
|
lenName, ## var lenName = int16(len(p.name))
|
||||||
|
newIdentNode("int16"),
|
||||||
|
newCall("int16", newCall("len", dotName)))),
|
||||||
|
newCall("writeData", streamID, newNimNode(nnkAddr).und(lenName), 2.lit),
|
||||||
|
newNimNode(nnkForStmt).und( ## for item in 0..length - 1: pack(p.name[item], stream)
|
||||||
|
item,
|
||||||
|
infix(0.lit, "..", infix(lenName, "-", 1.lit)),
|
||||||
|
newNimNode(nnkStmtList).und(
|
||||||
|
newCall("echo", item, ": ".lit),
|
||||||
|
newCall("pack", dotName[item], streamID))))
|
||||||
|
#set the default value to @[] (new sequence)
|
||||||
|
typeFields[i][2] = "@".prefix(newNimNode(nnkBracket))
|
||||||
|
else:
|
||||||
|
error("Unknown type: "& treeRepr(typeFields[i]))
|
||||||
|
of nnkIdent: ##normal type
|
||||||
|
case $typeFields[i][1].ident
|
||||||
|
of "string": # length encoded string
|
||||||
|
packBody.add(newCall("writeLEStr", streamID, dotName))
|
||||||
|
readBody.add(resName := newCall("readLEStr", streamID))
|
||||||
|
of "int8", "int16", "int32", "float32", "float64", "char", "bool":
|
||||||
|
packBody.add(newCall(
|
||||||
|
"writeData", streamID, newNimNode(nnkAddr).und(dotName), newCall("sizeof", dotName)))
|
||||||
|
readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID))
|
||||||
|
else: ## hopefully the type you specified was another defpacket() type
|
||||||
|
packBody.add(newCall("pack", dotName, streamID))
|
||||||
|
readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID))
|
||||||
|
else:
|
||||||
|
error("I dont know what to do with: "& treerepr(typeFields[i]))
|
||||||
|
|
||||||
|
var
|
||||||
|
toStringFunc = newNimNode(nnkProcDef).und(
|
||||||
|
newNimNode(nnkPostfix).und(
|
||||||
|
^"*",
|
||||||
|
newNimNode(nnkAccQuoted).und(^"$")),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkFormalParams).und(
|
||||||
|
^"string",
|
||||||
|
newNimNode(nnkIdentDefs).und(
|
||||||
|
packetID, # p: typeName
|
||||||
|
typeName,
|
||||||
|
emptyNode())),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkStmtList).und(#[6]
|
||||||
|
newNimNode(nnkAsgn).und(
|
||||||
|
^"result", ## result =
|
||||||
|
newNimNode(nnkCall).und(#[6][0][1]
|
||||||
|
^"format", ## format
|
||||||
|
emptyNode())))) ## "[TypeName $1 $2]"
|
||||||
|
formatStr = "["& $typeName.ident
|
||||||
|
|
||||||
|
const emptyFields = {nnkEmpty, nnkNilLit}
|
||||||
|
var objFields = newNimNode(nnkRecList)
|
||||||
|
for i in 0.. < len(typeFields):
|
||||||
|
let fname = typeFields[i][0]
|
||||||
|
constructorParams.add(newNimNode(nnkIdentDefs).und(
|
||||||
|
fname,
|
||||||
|
typeFields[i][1],
|
||||||
|
typeFields[i][2]))
|
||||||
|
constructorBody.add((^"result").dot(fname) := fname)
|
||||||
|
#export the name
|
||||||
|
typeFields[i][0] = fname.postfix("*")
|
||||||
|
if not(typeFields[i][2].kind in emptyFields):
|
||||||
|
## empty the type default for the type def
|
||||||
|
typeFields[i][2] = newNimNode(nnkEmpty)
|
||||||
|
objFields.add(typeFields[i])
|
||||||
|
toStringFunc[6][0][1].add(
|
||||||
|
prefix("$", packetID.dot(fname)))
|
||||||
|
formatStr.add " $"
|
||||||
|
formatStr.add($(i + 1))
|
||||||
|
|
||||||
|
formatStr.add ']'
|
||||||
|
toStringFunc[6][0][1][1] = formatStr.lit()
|
||||||
|
|
||||||
|
result.add(
|
||||||
|
newNimNode(nnkTypeSection).und(
|
||||||
|
newNimNode(nnkTypeDef).und(
|
||||||
|
typeName.postfix("*"),
|
||||||
|
newNimNode(nnkEmpty),
|
||||||
|
newNimNode(nnkObjectTy).und(
|
||||||
|
newNimNode(nnkEmpty), #not sure what this is
|
||||||
|
newNimNode(nnkEmpty), #parent: OfInherit(Ident(!"SomeObj"))
|
||||||
|
objFields))))
|
||||||
|
result.add(constructor.und(constructorBody))
|
||||||
|
result.add(pack.und(packBody))
|
||||||
|
result.add(read.und(readBody))
|
||||||
|
result.add(toStringFunc)
|
||||||
|
when defined(GenPacketShowOutput):
|
||||||
|
echo(repr(result))
|
||||||
|
|
||||||
|
proc `->`(a: string, b: string): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkIdentDefs).und(^a, ^b, newNimNode(nnkEmpty))
|
||||||
|
proc `->`(a: string, b: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkIdentDefs).und(^a, b, newNimNode(nnkEmpty))
|
||||||
|
proc `->`(a, b: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
a[2] = b
|
||||||
|
result = a
|
||||||
|
|
||||||
|
proc newProc*(name: string, params: varargs[PNimrodNode], resultType: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkProcDef).und(
|
||||||
|
^name,
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkFormalParams).und(resultType),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkStmtList))
|
||||||
|
result[3].add(params)
|
||||||
|
macro forwardPacket*(typeName: expr, underlyingType: typedesc): stmt {.immediate.} =
|
||||||
|
result = newNimNode(nnkStmtList).und(
|
||||||
|
newProc(
|
||||||
|
"read"& $typeName.ident,
|
||||||
|
["s" -> "PStream" -> newNimNode(nnkNilLit)],
|
||||||
|
typeName),
|
||||||
|
newProc(
|
||||||
|
"pack",
|
||||||
|
[ "p" -> newNimNode(nnkVarTy).und(typeName),
|
||||||
|
"s" -> "PStream" -> newNimNode(nnkNilLit)],
|
||||||
|
emptyNode()))
|
||||||
|
result[0][6].add(newNimNode(nnkDiscardStmt).und(
|
||||||
|
newCall(
|
||||||
|
"readData", ^"s", newNimNode(nnkAddr).und(^"result"), newCall("sizeof", ^"result")
|
||||||
|
)))
|
||||||
|
result[1][6].add(
|
||||||
|
newCall(
|
||||||
|
"writeData", ^"s", newNimNode(nnkAddr).und(^"p"), newCall(
|
||||||
|
"sizeof", ^"p")))
|
||||||
|
when defined(GenPacketShowOutput):
|
||||||
|
echo(repr(result))
|
||||||
|
|
||||||
|
template forwardPacketT*(typeName: expr): stmt {.dirty, immediate.} =
|
||||||
|
proc `read typeName`*(s: PStream): typeName =
|
||||||
|
discard readData(s, addr result, sizeof(result))
|
||||||
|
proc `pack typeName`*(p: var typeName; s: PStream) =
|
||||||
|
writeData(s, addr p, sizeof(p))
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
type
|
||||||
|
SomeEnum = enum
|
||||||
|
A = 0'i8,
|
||||||
|
B, C
|
||||||
|
forwardPacket(SomeEnum, int8)
|
||||||
|
|
||||||
|
|
||||||
|
defPacket(Foo, tuple[x: array[0..4, int8]])
|
||||||
|
var f = newFoo([4'i8, 3'i8, 2'i8, 1'i8, 0'i8])
|
||||||
|
var s2 = newStringStream("")
|
||||||
|
f.pack(s2)
|
||||||
|
assert s2.data == "\4\3\2\1\0"
|
||||||
|
|
||||||
|
var s = newStringStream()
|
||||||
|
s.flushImpl = proc(s: PStream) =
|
||||||
|
var z = PStringStream(s)
|
||||||
|
z.setPosition(0)
|
||||||
|
z.data.setLen(0)
|
||||||
|
|
||||||
|
|
||||||
|
s.setPosition(0)
|
||||||
|
s.data.setLen(0)
|
||||||
|
var o = B
|
||||||
|
o.pack(s)
|
||||||
|
o = A
|
||||||
|
o.pack(s)
|
||||||
|
o = C
|
||||||
|
o.pack(s)
|
||||||
|
assert s.data == "\1\0\2"
|
||||||
|
s.flush
|
||||||
|
|
||||||
|
defPacket(Y, tuple[z: int8])
|
||||||
|
proc `$`(z: Y): string = result = "Y("& $z.z &")"
|
||||||
|
defPacket(TestPkt, tuple[x: seq[Y]])
|
||||||
|
var test = newTestPkt()
|
||||||
|
test.x.add([newY(5), newY(4), newY(3), newY(2), newY(1)])
|
||||||
|
for itm in test.x:
|
||||||
|
echo(itm)
|
||||||
|
test.pack(s)
|
||||||
|
echo(repr(s.data))
|
||||||
|
|
@ -0,0 +1,296 @@
|
||||||
|
import macros, macro_dsl, estreams
|
||||||
|
from strutils import format
|
||||||
|
|
||||||
|
template newLenName(): stmt {.immediate.} =
|
||||||
|
let lenName {.inject.} = ^("len"& $lenNames)
|
||||||
|
inc(lenNames)
|
||||||
|
|
||||||
|
template defPacketImports*(): stmt {.immediate, dirty.} =
|
||||||
|
import macros, macro_dsl, estreams
|
||||||
|
from strutils import format
|
||||||
|
|
||||||
|
proc `$`*[T](x: seq[T]): string =
|
||||||
|
result = "[seq len="
|
||||||
|
result.add($x.len)
|
||||||
|
result.add ':'
|
||||||
|
for i in 0.. <len(x):
|
||||||
|
result.add " "
|
||||||
|
result.add($x[i])
|
||||||
|
result.add ']'
|
||||||
|
|
||||||
|
macro defPacket*(typeNameN: expr, typeFields: expr): stmt {.immediate.} =
|
||||||
|
result = newNimNode(nnkStmtList)
|
||||||
|
let
|
||||||
|
typeName = quoted2ident(typeNameN)
|
||||||
|
packetID = ^"p"
|
||||||
|
streamID = ^"s"
|
||||||
|
var
|
||||||
|
constructorParams = newNimNode(nnkFormalParams).und(typeName)
|
||||||
|
constructor = newNimNode(nnkProcDef).und(
|
||||||
|
postfix(^("new"& $typeName.ident), "*"),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
constructorParams,
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode())
|
||||||
|
pack = newNimNode(nnkProcDef).und(
|
||||||
|
postfix(^"pack", "*"),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkFormalParams).und(
|
||||||
|
emptyNode(), # : void
|
||||||
|
newNimNode(nnkIdentDefs).und(
|
||||||
|
streamID, # s: PBuffer
|
||||||
|
^"PBuffer",
|
||||||
|
newNimNode(nnkNilLit)),
|
||||||
|
newNimNode(nnkIdentDefs).und(
|
||||||
|
packetID, # p: var typeName
|
||||||
|
newNimNode(nnkVarTy).und(typeName),
|
||||||
|
emptyNode())),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode())
|
||||||
|
read = newNimNode(nnkProcDef).und(
|
||||||
|
newIdentNode("read"& $typeName.ident).postfix("*"),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkFormalParams).und(
|
||||||
|
typeName, #result type
|
||||||
|
newNimNode(nnkIdentDefs).und(
|
||||||
|
streamID, # s: PBuffer = nil
|
||||||
|
^"PBuffer",
|
||||||
|
newNimNode(nnkNilLit))),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode())
|
||||||
|
constructorBody = newNimNode(nnkStmtList)
|
||||||
|
packBody = newNimNode(nnkStmtList)
|
||||||
|
readBody = newNimNode(nnkStmtList)
|
||||||
|
lenNames = 0
|
||||||
|
for i in 0.. typeFields.len - 1:
|
||||||
|
let
|
||||||
|
name = typeFields[i][0]
|
||||||
|
dotName = packetID.dot(name)
|
||||||
|
resName = newIdentNode(!"result").dot(name)
|
||||||
|
case typeFields[i][1].kind
|
||||||
|
of nnkBracketExpr: #ex: paddedstring[32, '\0'], array[range, type]
|
||||||
|
case $typeFields[i][1][0].ident
|
||||||
|
of "seq":
|
||||||
|
## let lenX = readInt16(s)
|
||||||
|
newLenName()
|
||||||
|
let
|
||||||
|
item = ^"item" ## item name in our iterators
|
||||||
|
seqType = typeFields[i][1][1] ## type of seq
|
||||||
|
readName = newIdentNode("read"& $seqType.ident)
|
||||||
|
readBody.add(newNimNode(nnkLetSection).und(
|
||||||
|
newNimNode(nnkIdentDefs).und(
|
||||||
|
lenName,
|
||||||
|
newNimNode(nnkEmpty),
|
||||||
|
newCall("readInt16", streamID))))
|
||||||
|
readBody.add( ## result.name = @[]
|
||||||
|
resName := ("@".prefix(newNimNode(nnkBracket))),
|
||||||
|
newNimNode(nnkForStmt).und( ## for item in 1..len:
|
||||||
|
item,
|
||||||
|
infix(1.lit, "..", lenName),
|
||||||
|
newNimNode(nnkStmtList).und(
|
||||||
|
newCall( ## add(result.name, unpack[seqType](stream))
|
||||||
|
"add", resName, newNimNode(nnkCall).und(readName, streamID)
|
||||||
|
) ) ) )
|
||||||
|
packbody.add(
|
||||||
|
newNimNode(nnkVarSection).und(newNimNode(nnkIdentDefs).und(
|
||||||
|
lenName, ## var lenName = int16(len(p.name))
|
||||||
|
newIdentNode("int16"),
|
||||||
|
newCall("int16", newCall("len", dotName)))),
|
||||||
|
newCall("writeBE", streamID, lenName),
|
||||||
|
newNimNode(nnkForStmt).und( ## for item in 0..length - 1: pack(p.name[item], stream)
|
||||||
|
item,
|
||||||
|
infix(0.lit, "..", infix(lenName, "-", 1.lit)),
|
||||||
|
newNimNode(nnkStmtList).und(
|
||||||
|
newCall("echo", item, ": ".lit),
|
||||||
|
newCall("pack", streamID, dotName[item]))))
|
||||||
|
#set the default value to @[] (new sequence)
|
||||||
|
typeFields[i][2] = "@".prefix(newNimNode(nnkBracket))
|
||||||
|
else:
|
||||||
|
error("Unknown type: "& treeRepr(typeFields[i]))
|
||||||
|
of nnkIdent: ##normal type
|
||||||
|
case $typeFields[i][1].ident
|
||||||
|
of "string": # length encoded string
|
||||||
|
packBody.add(newCall("write", streamID, dotName))
|
||||||
|
readBody.add(resName := newCall("readStr", streamID))
|
||||||
|
of "int8", "int16", "int32", "float32", "float64", "char", "bool":
|
||||||
|
packBody.add(newCall(
|
||||||
|
"writeBE", streamID, dotName))
|
||||||
|
readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID))
|
||||||
|
else: ## hopefully the type you specified was another defpacket() type
|
||||||
|
packBody.add(newCall("pack", streamID, dotName))
|
||||||
|
readBody.add(resName := newCall("read"& $typeFields[i][1].ident, streamID))
|
||||||
|
else:
|
||||||
|
error("I dont know what to do with: "& treerepr(typeFields[i]))
|
||||||
|
|
||||||
|
var
|
||||||
|
toStringFunc = newNimNode(nnkProcDef).und(
|
||||||
|
newNimNode(nnkPostfix).und(
|
||||||
|
^"*",
|
||||||
|
newNimNode(nnkAccQuoted).und(^"$")),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkFormalParams).und(
|
||||||
|
^"string",
|
||||||
|
newNimNode(nnkIdentDefs).und(
|
||||||
|
packetID, # p: typeName
|
||||||
|
typeName,
|
||||||
|
emptyNode())),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkStmtList).und(#[6]
|
||||||
|
newNimNode(nnkAsgn).und(
|
||||||
|
^"result", ## result =
|
||||||
|
newNimNode(nnkCall).und(#[6][0][1]
|
||||||
|
^"format", ## format
|
||||||
|
emptyNode())))) ## "[TypeName $1 $2]"
|
||||||
|
formatStr = "["& $typeName.ident
|
||||||
|
|
||||||
|
const emptyFields = {nnkEmpty, nnkNilLit}
|
||||||
|
var objFields = newNimNode(nnkRecList)
|
||||||
|
for i in 0.. < len(typeFields):
|
||||||
|
let fname = typeFields[i][0]
|
||||||
|
constructorParams.add(newNimNode(nnkIdentDefs).und(
|
||||||
|
fname,
|
||||||
|
typeFields[i][1],
|
||||||
|
typeFields[i][2]))
|
||||||
|
constructorBody.add((^"result").dot(fname) := fname)
|
||||||
|
#export the name
|
||||||
|
typeFields[i][0] = fname.postfix("*")
|
||||||
|
if not(typeFields[i][2].kind in emptyFields):
|
||||||
|
## empty the type default for the type def
|
||||||
|
typeFields[i][2] = newNimNode(nnkEmpty)
|
||||||
|
objFields.add(typeFields[i])
|
||||||
|
toStringFunc[6][0][1].add(
|
||||||
|
prefix("$", packetID.dot(fname)))
|
||||||
|
formatStr.add " $"
|
||||||
|
formatStr.add($(i + 1))
|
||||||
|
|
||||||
|
formatStr.add ']'
|
||||||
|
toStringFunc[6][0][1][1] = formatStr.lit()
|
||||||
|
|
||||||
|
result.add(
|
||||||
|
newNimNode(nnkTypeSection).und(
|
||||||
|
newNimNode(nnkTypeDef).und(
|
||||||
|
typeName.postfix("*"),
|
||||||
|
newNimNode(nnkEmpty),
|
||||||
|
newNimNode(nnkObjectTy).und(
|
||||||
|
newNimNode(nnkEmpty), #not sure what this is
|
||||||
|
newNimNode(nnkEmpty), #parent: OfInherit(Ident(!"SomeObj"))
|
||||||
|
objFields))))
|
||||||
|
result.add(constructor.und(constructorBody))
|
||||||
|
result.add(pack.und(packBody))
|
||||||
|
result.add(read.und(readBody))
|
||||||
|
result.add(toStringFunc)
|
||||||
|
when defined(GenPacketShowOutput):
|
||||||
|
echo(repr(result))
|
||||||
|
|
||||||
|
proc newProc*(name: PNimrodNode; params: varargs[PNimrodNode]; resultType: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkProcDef).und(
|
||||||
|
name,
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkFormalParams).und(resultType),
|
||||||
|
emptyNode(),
|
||||||
|
emptyNode(),
|
||||||
|
newNimNode(nnkStmtList))
|
||||||
|
result[3].add(params)
|
||||||
|
|
||||||
|
proc body*(procNode: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
assert procNode.kind == nnkProcDef and procNode[6].kind == nnkStmtList
|
||||||
|
result = procNode[6]
|
||||||
|
|
||||||
|
proc iddefs*(a, b: string; c: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkIdentDefs).und(^a, ^b, c)
|
||||||
|
proc iddefs*(a: string; b: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkIdentDefs).und(^a, b, emptyNode())
|
||||||
|
proc varTy*(a: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkVarTy).und(a)
|
||||||
|
|
||||||
|
macro forwardPacket*(typeName: expr, underlyingType: expr): stmt {.immediate.} =
|
||||||
|
var
|
||||||
|
packetID = ^"p"
|
||||||
|
streamID = ^"s"
|
||||||
|
result = newNimNode(nnkStmtList).und(
|
||||||
|
newProc(
|
||||||
|
(^("read"& $typeName.ident)).postfix("*"),
|
||||||
|
[ iddefs("s", "PBuffer", newNimNode(nnkNilLit)) ],
|
||||||
|
typeName),
|
||||||
|
newProc(
|
||||||
|
(^"pack").postfix("*"),
|
||||||
|
[ iddefs("s", "PBuffer", newNimNode(nnkNilLit)),
|
||||||
|
iddefs("p", varTy(typeName)) ],
|
||||||
|
emptyNode()))
|
||||||
|
var
|
||||||
|
readBody = result[0][6]
|
||||||
|
packBody = result[1][6]
|
||||||
|
resName = ^"result"
|
||||||
|
|
||||||
|
case underlyingType.kind
|
||||||
|
of nnkBracketExpr:
|
||||||
|
case $underlyingType[0].ident
|
||||||
|
of "array":
|
||||||
|
for i in underlyingType[1][1].intval.int .. underlyingType[1][2].intval.int:
|
||||||
|
readBody.add(
|
||||||
|
newCall("read", ^"s", resName[lit(i)]))
|
||||||
|
packBody.add(
|
||||||
|
newCall("writeBE", ^"s", packetID[lit(i)]))
|
||||||
|
else:
|
||||||
|
echo "Unknown type: ", repr(underlyingtype)
|
||||||
|
else:
|
||||||
|
echo "unknown type:", repr(underlyingtype)
|
||||||
|
echo(repr(result))
|
||||||
|
|
||||||
|
template forwardPacketT*(typeName: expr; underlyingType: expr): stmt {.dirty, immediate.} =
|
||||||
|
proc `read typeName`*(buffer: PBuffer): typeName =
|
||||||
|
#discard readData(s, addr result, sizeof(result))
|
||||||
|
var res: underlyingType
|
||||||
|
buffer.read(res)
|
||||||
|
result = typeName(res)
|
||||||
|
proc `pack`*(buffer: PBuffer; ord: var typeName) =
|
||||||
|
#writeData(s, addr p, sizeof(p))
|
||||||
|
buffer.write(underlyingType(ord))
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
type
|
||||||
|
SomeEnum = enum
|
||||||
|
A = 0'i8,
|
||||||
|
B, C
|
||||||
|
forwardPacket(SomeEnum, int8)
|
||||||
|
|
||||||
|
|
||||||
|
defPacket(Foo, tuple[x: array[0..4, int8]])
|
||||||
|
var f = newFoo([4'i8, 3'i8, 2'i8, 1'i8, 0'i8])
|
||||||
|
var s2 = newStringStream("")
|
||||||
|
f.pack(s2)
|
||||||
|
assert s2.data == "\4\3\2\1\0"
|
||||||
|
|
||||||
|
var s = newStringStream()
|
||||||
|
s.flushImpl = proc(s: PStream) =
|
||||||
|
var z = PStringStream(s)
|
||||||
|
z.setPosition(0)
|
||||||
|
z.data.setLen(0)
|
||||||
|
|
||||||
|
|
||||||
|
s.setPosition(0)
|
||||||
|
s.data.setLen(0)
|
||||||
|
var o = B
|
||||||
|
o.pack(s)
|
||||||
|
o = A
|
||||||
|
o.pack(s)
|
||||||
|
o = C
|
||||||
|
o.pack(s)
|
||||||
|
assert s.data == "\1\0\2"
|
||||||
|
s.flush
|
||||||
|
|
||||||
|
defPacket(Y, tuple[z: int8])
|
||||||
|
proc `$`(z: Y): string = result = "Y("& $z.z &")"
|
||||||
|
defPacket(TestPkt, tuple[x: seq[Y]])
|
||||||
|
var test = newTestPkt()
|
||||||
|
test.x.add([newY(5), newY(4), newY(3), newY(2), newY(1)])
|
||||||
|
for itm in test.x:
|
||||||
|
echo(itm)
|
||||||
|
test.pack(s)
|
||||||
|
echo(repr(s.data))
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
import macros
|
||||||
|
{.deadCodeElim: on.}
|
||||||
|
#Inline macro.add() to allow for easier nesting
|
||||||
|
proc und*(a: PNimrodNode; b: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
a.add(b)
|
||||||
|
result = a
|
||||||
|
proc und*(a: PNimrodNode; b: varargs[PNimrodNode]): PNimrodNode {.compileTime.} =
|
||||||
|
a.add(b)
|
||||||
|
result = a
|
||||||
|
|
||||||
|
proc `^`*(a: string): PNimrodNode {.compileTime.} =
|
||||||
|
## new ident node
|
||||||
|
result = newIdentNode(!a)
|
||||||
|
proc `[]`*(a, b: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## new bracket expression: node[node] not to be confused with node[indx]
|
||||||
|
result = newNimNode(nnkBracketExpr).und(a, b)
|
||||||
|
proc `:=`*(left, right: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## new Asgn node: left = right
|
||||||
|
result = newNimNode(nnkAsgn).und(left, right)
|
||||||
|
|
||||||
|
proc lit*(a: string): PNimrodNode {.compileTime.} =
|
||||||
|
result = newStrLitNode(a)
|
||||||
|
proc lit*(a: int): PNimrodNode {.compileTime.} =
|
||||||
|
result = newIntLitNode(a)
|
||||||
|
proc lit*(a: float): PNimrodNode {.compileTime.} =
|
||||||
|
result = newFloatLitNode(a)
|
||||||
|
proc lit*(a: char): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkCharLit)
|
||||||
|
result.intval = a.ord
|
||||||
|
|
||||||
|
proc emptyNode*(): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkEmpty)
|
||||||
|
|
||||||
|
proc dot*(left, right: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkDotExpr).und(left, right)
|
||||||
|
proc postfix*(a: PNimrodNode, b: string): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkPostfix).und(newIdentNode(!b), a)
|
||||||
|
proc prefix*(a: string, b: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
result = newNimNode(nnkPrefix).und(newIdentNode(!a), b)
|
||||||
|
|
||||||
|
proc infix*(a, b, c: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## 5.infix("+", 10) ## => 5 + 10
|
||||||
|
result = newNimNode(nnkInfix).und(b, a, c)
|
||||||
|
proc infix*(a: PNimrodNode; b: string; c: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
## Infix operation: infix(5, "+", 10) ## =>
|
||||||
|
result = newNimNode(nnkInfix).und(newIdentNode(b), a, c)
|
||||||
|
|
||||||
|
proc quoted2ident*(a: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||||
|
if a.kind != nnkAccQuoted:
|
||||||
|
return a
|
||||||
|
var pname = ""
|
||||||
|
for piece in 0..a.len - 1:
|
||||||
|
pname.add($a[piece].ident)
|
||||||
|
result = ^pname
|
||||||
|
|
||||||
|
|
||||||
|
macro `?`(a: expr): expr =
|
||||||
|
## Character literal ?A #=> 'A'
|
||||||
|
result = ($a[1].ident)[0].lit
|
||||||
|
## echo(?F,?a,?t,?t,?y)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
macro foo(x: stmt): stmt =
|
||||||
|
result = newNimNode(nnkStmtList)
|
||||||
|
result.add(newNimNode(nnkCall).und(!!"echo", "Hello thar".lit))
|
||||||
|
result.add(newCall("echo", lit("3 * 45 = "), (3.lit.infix("*", 45.lit))))
|
||||||
|
let stmtlist = x[1]
|
||||||
|
for i in countdown(len(stmtlist)-1, 0):
|
||||||
|
result.add(stmtlist[i])
|
||||||
|
foo:
|
||||||
|
echo y, " * 2 = ", y * 2
|
||||||
|
let y = 320
|
||||||
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import streams
|
||||||
|
from strutils import repeatChar
|
||||||
|
|
||||||
|
proc readPaddedStr*(s: PStream, length: int, padChar = '\0'): TaintedString =
|
||||||
|
var lastChr = length
|
||||||
|
result = s.readStr(length)
|
||||||
|
while lastChr >= 0 and result[lastChr - 1] == padChar: dec(lastChr)
|
||||||
|
result.setLen(lastChr)
|
||||||
|
|
||||||
|
proc writePaddedStr*(s: PStream, str: string, length: int, padChar = '\0') =
|
||||||
|
if str.len < length:
|
||||||
|
s.write(str)
|
||||||
|
s.write(repeatChar(length - str.len, padChar))
|
||||||
|
elif str.len > length:
|
||||||
|
s.write(str.substr(0, length - 1))
|
||||||
|
else:
|
||||||
|
s.write(str)
|
||||||
|
|
||||||
|
proc readLEStr*(s: PStream): TaintedString =
|
||||||
|
var len = s.readInt16()
|
||||||
|
result = s.readStr(len)
|
||||||
|
|
||||||
|
proc writeLEStr*(s: PStream, str: string) =
|
||||||
|
s.write(str.len.int16)
|
||||||
|
s.write(str)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
var testStream = newStringStream()
|
||||||
|
|
||||||
|
testStream.writeLEStr("Hello")
|
||||||
|
doAssert testStream.data == "\5\0Hello"
|
||||||
|
|
||||||
|
testStream.setPosition 0
|
||||||
|
var res = testStream.readLEStr()
|
||||||
|
doAssert res == "Hello"
|
||||||
|
|
||||||
|
testStream.setPosition 0
|
||||||
|
testStream.writePaddedStr("Sup", 10)
|
||||||
|
echo(repr(testStream), testStream.data.len)
|
||||||
|
doAssert testStream.data == "Sup"&repeatChar(7, '\0')
|
||||||
|
|
||||||
|
testStream.setPosition 0
|
||||||
|
res = testStream.readPaddedStr(10)
|
||||||
|
doAssert res == "Sup"
|
||||||
|
|
||||||
|
testStream.close()
|
||||||
|
|
||||||
83
tests/manyloc/keineschweine/dependencies/nake/nake.nim
Normal file
83
tests/manyloc/keineschweine/dependencies/nake/nake.nim
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
discard """
|
||||||
|
DO AS THOU WILST PUBLIC LICENSE
|
||||||
|
|
||||||
|
Whoever should stumble upon this document is henceforth and forever
|
||||||
|
entitled to DO AS THOU WILST with aforementioned document and the
|
||||||
|
contents thereof.
|
||||||
|
|
||||||
|
As said in the Olde Country, `Keepe it Gangster'."""
|
||||||
|
|
||||||
|
import strutils, parseopt, tables, os
|
||||||
|
|
||||||
|
type
|
||||||
|
PTask* = ref object
|
||||||
|
desc*: string
|
||||||
|
action*: TTaskFunction
|
||||||
|
TTaskFunction* = proc() {.closure.}
|
||||||
|
var
|
||||||
|
tasks* = initTable[string, PTask](16)
|
||||||
|
|
||||||
|
proc newTask*(desc: string; action: TTaskFunction): PTask
|
||||||
|
proc runTask*(name: string) {.inline.}
|
||||||
|
proc shell*(cmd: varargs[string, `$`]): int {.discardable.}
|
||||||
|
proc cd*(dir: string) {.inline.}
|
||||||
|
|
||||||
|
template nakeImports*(): stmt {.immediate.} =
|
||||||
|
import tables, parseopt, strutils, os
|
||||||
|
|
||||||
|
template task*(name: string; description: string; body: stmt): stmt {.dirty, immediate.} =
|
||||||
|
block:
|
||||||
|
var t = newTask(description, proc() {.closure.} =
|
||||||
|
body)
|
||||||
|
tasks[name] = t
|
||||||
|
|
||||||
|
proc newTask*(desc: string; action: TTaskFunction): PTask =
|
||||||
|
new(result)
|
||||||
|
result.desc = desc
|
||||||
|
result.action = action
|
||||||
|
proc runTask*(name: string) = tasks[name].action()
|
||||||
|
|
||||||
|
proc shell*(cmd: varargs[string, `$`]): int =
|
||||||
|
result = execShellCmd(cmd.join(" "))
|
||||||
|
proc cd*(dir: string) = setCurrentDir(dir)
|
||||||
|
template withDir*(dir: string; body: stmt): stmt =
|
||||||
|
## temporary cd
|
||||||
|
## withDir "foo":
|
||||||
|
## # inside foo
|
||||||
|
## #back to last dir
|
||||||
|
var curDir = getCurrentDir()
|
||||||
|
cd(dir)
|
||||||
|
body
|
||||||
|
cd(curDir)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
if not existsFile("nakefile.nim"):
|
||||||
|
echo "No nakefile.nim found. Current working dir is ", getCurrentDir()
|
||||||
|
quit 1
|
||||||
|
var args = ""
|
||||||
|
for i in 1..paramCount():
|
||||||
|
args.add paramStr(i)
|
||||||
|
args.add " "
|
||||||
|
quit(shell("nimrod", "c", "-r", "nakefile.nim", args))
|
||||||
|
else:
|
||||||
|
addQuitProc(proc() {.noconv.} =
|
||||||
|
var
|
||||||
|
task: string
|
||||||
|
printTaskList: bool
|
||||||
|
for kind, key, val in getOpt():
|
||||||
|
case kind
|
||||||
|
of cmdLongOption, cmdShortOption:
|
||||||
|
case key.tolower
|
||||||
|
of "tasks", "t":
|
||||||
|
printTaskList = true
|
||||||
|
else:
|
||||||
|
echo "Unknown option: ", key, ": ", val
|
||||||
|
of cmdArgument:
|
||||||
|
task = key
|
||||||
|
else: nil
|
||||||
|
if printTaskList or task.isNil or not(tasks.hasKey(task)):
|
||||||
|
echo "Available tasks:"
|
||||||
|
for name, task in pairs(tasks):
|
||||||
|
echo name, " - ", task.desc
|
||||||
|
quit 0
|
||||||
|
tasks[task].action())
|
||||||
23
tests/manyloc/keineschweine/dependencies/nake/nakefile.nim
Normal file
23
tests/manyloc/keineschweine/dependencies/nake/nakefile.nim
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import nake
|
||||||
|
nakeImports
|
||||||
|
|
||||||
|
task "install", "compile and install nake binary":
|
||||||
|
if shell("nimrod", "c", "nake") == 0:
|
||||||
|
let path = getEnv("PATH").split(PathSep)
|
||||||
|
for index, dir in pairs(path):
|
||||||
|
echo " ", index, ". ", dir
|
||||||
|
echo "Where to install nake binary? (quit with ^C or quit or exit)"
|
||||||
|
let ans = stdin.readLine().toLower
|
||||||
|
var index = 0
|
||||||
|
case ans
|
||||||
|
of "q", "quit", "x", "exit":
|
||||||
|
quit 0
|
||||||
|
else:
|
||||||
|
index = parseInt(ans)
|
||||||
|
if index > path.len or index < 0:
|
||||||
|
echo "Invalid index."
|
||||||
|
quit 1
|
||||||
|
moveFile "nake", path[index]/"nake"
|
||||||
|
echo "Great success!"
|
||||||
|
|
||||||
|
|
||||||
13
tests/manyloc/keineschweine/dependencies/sfml/README.md
Normal file
13
tests/manyloc/keineschweine/dependencies/sfml/README.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
sfml-nimrod
|
||||||
|
===========
|
||||||
|
|
||||||
|
Nimrod binding of SFML 2.0
|
||||||
|
|
||||||
|
This is only tested for Linux at the moment
|
||||||
|
|
||||||
|
### What is needed for Windows / OS X?
|
||||||
|
|
||||||
|
* The library names need filling in
|
||||||
|
* TWindowHandle is handled differently on those platforms
|
||||||
|
|
||||||
|
I believe that is it
|
||||||
1115
tests/manyloc/keineschweine/dependencies/sfml/sfml.nim
Normal file
1115
tests/manyloc/keineschweine/dependencies/sfml/sfml.nim
Normal file
File diff suppressed because it is too large
Load diff
899
tests/manyloc/keineschweine/dependencies/sfml/sfml_audio.nim
Normal file
899
tests/manyloc/keineschweine/dependencies/sfml/sfml_audio.nim
Normal file
|
|
@ -0,0 +1,899 @@
|
||||||
|
import
|
||||||
|
sfml
|
||||||
|
const
|
||||||
|
Lib = "libcsfml-audio.so.2.0"
|
||||||
|
type
|
||||||
|
PMusic* = ptr TMusic
|
||||||
|
TMusic* {.pure, final.} = object
|
||||||
|
PSound* = ptr TSound
|
||||||
|
TSound* {.pure, final.} = object
|
||||||
|
PSoundBuffer* = ptr TSoundBuffer
|
||||||
|
TSoundBuffer* {.pure, final.} = object
|
||||||
|
PSoundBufferRecorder* = ptr TSoundBufferRecorder
|
||||||
|
TSoundBufferRecorder* {.pure, final.} = object
|
||||||
|
PSoundRecorder* = ptr TSoundRecorder
|
||||||
|
TSoundRecorder* {.pure, final.} = object
|
||||||
|
PSoundStream* = ptr TSoundStream
|
||||||
|
TSoundStream* {.pure, final.} = object
|
||||||
|
TSoundStatus* {.size: sizeof(cint).} = enum
|
||||||
|
Stopped, Paused, Playing
|
||||||
|
|
||||||
|
proc newMusic*(filename: cstring): PMusic {.
|
||||||
|
cdecl, importc: "sfMusic_createFromFile", dynlib: Lib.}
|
||||||
|
proc newMusic*(data: pointer, size: cint): PMusic {.
|
||||||
|
cdecl, importc: "sfMusic_createFromMemory", dynlib: Lib.}
|
||||||
|
proc newMusic*(stream: PInputStream): PMusic {.
|
||||||
|
cdecl, importc: "sfMusic_createFromStream", dynlib: Lib.}
|
||||||
|
proc destroy*(music: PMusic) {.
|
||||||
|
cdecl, importc: "sfMusic_destroy", dynlib: Lib.}
|
||||||
|
proc setLoop*(music: PMusic, loop: bool) {.
|
||||||
|
cdecl, importc: "sfMusic_setLoop", dynlib: Lib.}
|
||||||
|
proc getLoop*(music: PMusic): bool {.
|
||||||
|
cdecl, importc: "sfMusic_getLoop", dynlib: Lib.}
|
||||||
|
proc getDuration*(music: PMusic): TTime {.
|
||||||
|
cdecl, importc: "sfMusic_getDuration", dynlib: Lib.}
|
||||||
|
proc play*(music: PMusic) {.
|
||||||
|
cdecl, importc: "sfMusic_play", dynlib: Lib.}
|
||||||
|
proc pause*(music: PMusic) {.
|
||||||
|
cdecl, importc: "sfMusic_pause", dynlib: Lib.}
|
||||||
|
proc stop*(music: PMusic) {.
|
||||||
|
cdecl, importc: "sfMusic_stop", dynlib: Lib.}
|
||||||
|
proc getChannelCount*(music: PMusic): cint {.
|
||||||
|
cdecl, importc: "sfMusic_getChannelCount", dynlib: Lib.}
|
||||||
|
proc getSampleRate*(music: PMusic): cint {.
|
||||||
|
cdecl, importc: "sfMusic_getSampleRate", dynlib: Lib.}
|
||||||
|
proc getStatus*(music: PMusic): TSoundStatus {.
|
||||||
|
cdecl, importc: "sfMusic_getStatus", dynlib: Lib.}
|
||||||
|
proc getPlayingOffset*(music: PMusic): TTime {.
|
||||||
|
cdecl, importc: "sfMusic_getPlayingOffset", dynlib: Lib.}
|
||||||
|
proc setPitch*(music: PMusic, pitch: cfloat) {.
|
||||||
|
cdecl, importc: "sfMusic_setPitch", dynlib: Lib.}
|
||||||
|
proc setVolume*(music: PMusic, volume: float) {.
|
||||||
|
cdecl, importc: "sfMusic_setVolume", dynlib: Lib.}
|
||||||
|
proc setPosition*(music: PMusic, position: TVector3f) {.
|
||||||
|
cdecl, importc: "sfMusic_setPosition", dynlib: Lib.}
|
||||||
|
proc setRelativeToListener*(music: PMusic, relative: bool) {.
|
||||||
|
cdecl, importc: "sfMusic_setRelativeToListener", dynlib: Lib.}
|
||||||
|
proc setMinDistance*(music: PMusic, distance: cfloat) {.
|
||||||
|
cdecl, importc: "sfMusic_setMinDistance", dynlib: Lib.}
|
||||||
|
proc setAttenuation*(music: PMusic, attenuation: cfloat) {.
|
||||||
|
cdecl, importc: "sfMusic_setAttenuation", dynlib: Lib.}
|
||||||
|
proc setPlayingOffset*(music: PMusic, time: TTime) {.
|
||||||
|
cdecl, importc: "sfMusic_setPlayingOffset", dynlib: Lib.}
|
||||||
|
proc getPitch*(music: PMusic): cfloat {.
|
||||||
|
cdecl, importc: "sfMusic_getPitch", dynlib: Lib.}
|
||||||
|
proc getVolume*(music: PMusic): cfloat {.
|
||||||
|
cdecl, importc: "sfMusic_getVolume", dynlib: Lib.}
|
||||||
|
proc getPosition*(music: PMusic): TVector3f {.
|
||||||
|
cdecl, importc: "sfMusic_getPosition", dynlib: Lib.}
|
||||||
|
proc isRelativeToListener*(music: PMusic): bool {.
|
||||||
|
cdecl, importc: "sfMusic_isRelativeToListener", dynlib: Lib.}
|
||||||
|
proc getMinDistance*(music: PMusic): cfloat {.
|
||||||
|
cdecl, importc: "sfMusic_isRelativeToListener", dynlib: Lib.}
|
||||||
|
proc getAttenuation*(music: PMusic): cfloat {.
|
||||||
|
cdecl, importc: "sfMusic_isRelativeToListener", dynlib: Lib.}
|
||||||
|
|
||||||
|
#/ \brief Create a new sound
|
||||||
|
proc newSound*(): PSound{.
|
||||||
|
cdecl, importc: "sfSound_create", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Create a new sound by copying an existing one
|
||||||
|
#/
|
||||||
|
#/ \param sound Sound to copy
|
||||||
|
#/
|
||||||
|
#/ \return A new sfSound object which is a copy of \a sound
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc copy*(sound: PSound): PSound{.
|
||||||
|
cdecl, importc: "sfSound_copy", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Destroy a sound
|
||||||
|
proc destroy*(sound: PSound){.
|
||||||
|
cdecl, importc: "sfSound_destroy", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Start or resume playing a sound
|
||||||
|
#/
|
||||||
|
#/ This function starts the sound if it was stopped, resumes
|
||||||
|
#/ it if it was paused, and restarts it from beginning if it
|
||||||
|
#/ was it already playing.
|
||||||
|
#/ This function uses its own thread so that it doesn't block
|
||||||
|
#/ the rest of the program while the sound is played.
|
||||||
|
proc play*(sound: PSound){.
|
||||||
|
cdecl, importc: "sfSound_play", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ This function pauses the sound if it was playing,
|
||||||
|
#/ otherwise (sound already paused or stopped) it has no effect.
|
||||||
|
proc pause*(sound: PSound){.
|
||||||
|
cdecl, importc: "sfSound_pause", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ This function stops the sound if it was playing or paused,
|
||||||
|
#/ and does nothing if it was already stopped.
|
||||||
|
#/ It also resets the playing position (unlike sfSound_pause).
|
||||||
|
proc stop*(sound: PSound){.
|
||||||
|
cdecl, importc: "sfSound_stop", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ It is important to note that the sound buffer is not copied,
|
||||||
|
#/ thus the sfSoundBuffer object must remain alive as long
|
||||||
|
#/ as it is attached to the sound.
|
||||||
|
proc setBuffer*(sound: PSound; buffer: PSoundBuffer){.
|
||||||
|
cdecl, importc: "sfSound_setBuffer", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the audio buffer attached to a sound
|
||||||
|
proc getBuffer*(sound: PSound): PSoundBuffer{.
|
||||||
|
cdecl, importc: "sfSound_getBuffer", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set whether or not a sound should loop after reaching the end
|
||||||
|
#/
|
||||||
|
#/ If set, the sound will restart from beginning after
|
||||||
|
#/ reaching the end and so on, until it is stopped or
|
||||||
|
#/ sfSound_setLoop(sound, sfFalse) is called.
|
||||||
|
#/ The default looping state for sounds is false.
|
||||||
|
proc setLoop*(sound: PSound; loop: bool){.
|
||||||
|
cdecl, importc: "sfSound_setLoop", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Tell whether or not a soud is in loop mode
|
||||||
|
proc getLoop*(sound: PSound): bool {.
|
||||||
|
cdecl, importc: "sfSound_getLoop", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the current status of a sound (stopped, paused, playing)
|
||||||
|
proc getStatus*(sound: PSound): TSoundStatus{.
|
||||||
|
cdecl, importc: "sfSound_getStatus", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the pitch of a sound
|
||||||
|
#/
|
||||||
|
#/ The pitch represents the perceived fundamental frequency
|
||||||
|
#/ of a sound; thus you can make a sound more acute or grave
|
||||||
|
#/ by changing its pitch. A side effect of changing the pitch
|
||||||
|
#/ is to modify the playing speed of the sound as well.
|
||||||
|
#/ The default value for the pitch is 1.
|
||||||
|
proc setPitch*(sound: PSound; pitch: cfloat){.
|
||||||
|
cdecl, importc: "sfSound_setPitch", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the volume of a sound
|
||||||
|
#/
|
||||||
|
#/ The volume is a value between 0 (mute) and 100 (full volume).
|
||||||
|
#/ The default value for the volume is 100.
|
||||||
|
proc setVolume*(sound: PSound; volume: cfloat){.
|
||||||
|
cdecl, importc: "sfSound_setVolume", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the 3D position of a sound in the audio scene
|
||||||
|
#/
|
||||||
|
#/ Only sounds with one channel (mono sounds) can be
|
||||||
|
#/ spatialized.
|
||||||
|
#/ The default position of a sound is (0, 0, 0).
|
||||||
|
proc setPosition*(sound: PSound; position: TVector3f){.
|
||||||
|
cdecl, importc: "sfSound_setPosition", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Make the sound's position relative to the listener or absolute
|
||||||
|
#/
|
||||||
|
#/ Making a sound relative to the listener will ensure that it will always
|
||||||
|
#/ be played the same way regardless the position of the listener.
|
||||||
|
#/ This can be useful for non-spatialized sounds, sounds that are
|
||||||
|
#/ produced by the listener, or sounds attached to it.
|
||||||
|
#/ The default value is false (position is absolute).
|
||||||
|
proc setRelativeToListener*(sound: PSound; relative: bool){.
|
||||||
|
cdecl, importc: "sfSound_setRelativeToListener", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the minimum distance of a sound
|
||||||
|
#/
|
||||||
|
#/ The "minimum distance" of a sound is the maximum
|
||||||
|
#/ distance at which it is heard at its maximum volume. Further
|
||||||
|
#/ than the minimum distance, it will start to fade out according
|
||||||
|
#/ to its attenuation factor. A value of 0 ("inside the head
|
||||||
|
#/ of the listener") is an invalid value and is forbidden.
|
||||||
|
#/ The default value of the minimum distance is 1.
|
||||||
|
proc setMinDistance*(sound: PSound; distance: cfloat){.
|
||||||
|
cdecl, importc: "sfSound_setMinDistance", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the attenuation factor of a sound
|
||||||
|
#/
|
||||||
|
#/ The attenuation is a multiplicative factor which makes
|
||||||
|
#/ the sound more or less loud according to its distance
|
||||||
|
#/ from the listener. An attenuation of 0 will produce a
|
||||||
|
#/ non-attenuated sound, i.e. its volume will always be the same
|
||||||
|
#/ whether it is heard from near or from far. On the other hand,
|
||||||
|
#/ an attenuation value such as 100 will make the sound fade out
|
||||||
|
#/ very quickly as it gets further from the listener.
|
||||||
|
#/ The default value of the attenuation is 1.
|
||||||
|
proc setAttenuation*(sound: PSound; attenuation: cfloat){.
|
||||||
|
cdecl, importc: "sfSound_setAttenuation", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Change the current playing position of a sound
|
||||||
|
#/
|
||||||
|
#/ The playing position can be changed when the sound is
|
||||||
|
#/ either paused or playing.
|
||||||
|
proc setPlayingOffset*(sound: PSound; timeOffset: sfml.TTime){.
|
||||||
|
cdecl, importc: "sfSound_setPlayingOffset", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the pitch of a sound
|
||||||
|
proc getPitch*(sound: PSound): cfloat{.
|
||||||
|
cdecl, importc: "sfSound_getPitch", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the volume of a sound
|
||||||
|
proc getVolume*(sound: PSound): cfloat{.
|
||||||
|
cdecl, importc: "sfSound_getVolume", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the 3D position of a sound in the audio scene
|
||||||
|
proc getPosition*(sound: PSound): TVector3f{.
|
||||||
|
cdecl, importc: "sfSound_getPosition", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Tell whether a sound's position is relative to the
|
||||||
|
#/ listener or is absolute
|
||||||
|
proc isRelativeToListener*(sound: PSound): bool{.
|
||||||
|
cdecl, importc: "sfSound_isRelativeToListener", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the minimum distance of a sound
|
||||||
|
proc getMinDistance*(sound: PSound): cfloat{.
|
||||||
|
cdecl, importc: "sfSound_getMinDistance", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the attenuation factor of a sound
|
||||||
|
proc getAttenuation*(sound: PSound): cfloat{.
|
||||||
|
cdecl, importc: "sfSound_getAttenuation", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the current playing position of a sound
|
||||||
|
proc getPlayingOffset*(sound: PSound): TTime{.
|
||||||
|
cdecl, importc: "sfSound_getPlayingOffset", dynlib: Lib.}
|
||||||
|
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
# Headers
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Create a new sound buffer and load it from a file
|
||||||
|
#/
|
||||||
|
#/ Here is a complete list of all the supported audio formats:
|
||||||
|
#/ ogg, wav, flac, aiff, au, raw, paf, svx, nist, voc, ircam,
|
||||||
|
#/ w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64.
|
||||||
|
#/
|
||||||
|
#/ \param filename Path of the sound file to load
|
||||||
|
#/
|
||||||
|
#/ \return A new sfSoundBuffer object (NULL if failed)
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc newSoundBuffer*(filename: cstring): PSoundBuffer{.
|
||||||
|
cdecl, importc: "sfSoundBuffer_createFromFile", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Create a new sound buffer and load it from a file in memory
|
||||||
|
#/
|
||||||
|
#/ Here is a complete list of all the supported audio formats:
|
||||||
|
#/ ogg, wav, flac, aiff, au, raw, paf, svx, nist, voc, ircam,
|
||||||
|
#/ w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64.
|
||||||
|
#/
|
||||||
|
#/ \param data Pointer to the file data in memory
|
||||||
|
#/ \param sizeInBytes Size of the data to load, in bytes
|
||||||
|
#/
|
||||||
|
#/ \return A new sfSoundBuffer object (NULL if failed)
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc newSoundBuffer*(data: pointer; sizeInBytes: cint): PSoundBuffer{.
|
||||||
|
cdecl, importc: "sfSoundBuffer_createFromMemory", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Create a new sound buffer and load it from a custom stream
|
||||||
|
#/
|
||||||
|
#/ Here is a complete list of all the supported audio formats:
|
||||||
|
#/ ogg, wav, flac, aiff, au, raw, paf, svx, nist, voc, ircam,
|
||||||
|
#/ w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64.
|
||||||
|
#/
|
||||||
|
#/ \param stream Source stream to read from
|
||||||
|
#/
|
||||||
|
#/ \return A new sfSoundBuffer object (NULL if failed)
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc newSoundBuffer*(stream: PInputStream): PSoundBuffer{.
|
||||||
|
cdecl, importc: "sfSoundBuffer_createFromStream", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Create a new sound buffer and load it from an array of samples in memory
|
||||||
|
#/
|
||||||
|
#/ The assumed format of the audio samples is 16 bits signed integer
|
||||||
|
#/ (sfInt16).
|
||||||
|
#/
|
||||||
|
#/ \param samples Pointer to the array of samples in memory
|
||||||
|
#/ \param sampleCount Number of samples in the array
|
||||||
|
#/ \param channelCount Number of channels (1 = mono, 2 = stereo, ...)
|
||||||
|
#/ \param sampleRate Sample rate (number of samples to play per second)
|
||||||
|
#/
|
||||||
|
#/ \return A new sfSoundBuffer object (NULL if failed)
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc createFromSamples*(samples: ptr int16; sampleCount: cuint;
|
||||||
|
channelCount: cuint; sampleRate: cuint): PSoundBuffer{.
|
||||||
|
cdecl, importc: "sfSoundBuffer_createFromSamples", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Create a new sound buffer by copying an existing one
|
||||||
|
#/
|
||||||
|
#/ \param soundBuffer Sound buffer to copy
|
||||||
|
#/
|
||||||
|
#/ \return A new sfSoundBuffer object which is a copy of \a soundBuffer
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc copy*(soundBuffer: PSoundBuffer): PSoundBuffer{.
|
||||||
|
cdecl, importc: "sfSoundBuffer_copy", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Destroy a sound buffer
|
||||||
|
#/
|
||||||
|
#/ \param soundBuffer Sound buffer to destroy
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc destroy*(soundBuffer: PSoundBuffer){.
|
||||||
|
cdecl, importc: "sfSoundBuffer_destroy", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Save a sound buffer to an audio file
|
||||||
|
#/
|
||||||
|
#/ Here is a complete list of all the supported audio formats:
|
||||||
|
#/ ogg, wav, flac, aiff, au, raw, paf, svx, nist, voc, ircam,
|
||||||
|
#/ w64, mat4, mat5 pvf, htk, sds, avr, sd2, caf, wve, mpc2k, rf64.
|
||||||
|
#/
|
||||||
|
#/ \param soundBuffer Sound buffer object
|
||||||
|
#/ \param filename Path of the sound file to write
|
||||||
|
#/
|
||||||
|
#/ \return sfTrue if saving succeeded, sfFalse if it failed
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc saveToFile*(soundBuffer: PSoundBuffer; filename: cstring): bool {.
|
||||||
|
cdecl, importc: "sfSoundBuffer_saveToFile", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the array of audio samples stored in a sound buffer
|
||||||
|
#/
|
||||||
|
#/ The format of the returned samples is 16 bits signed integer
|
||||||
|
#/ (sfInt16). The total number of samples in this array
|
||||||
|
#/ is given by the sfSoundBuffer_getSampleCount function.
|
||||||
|
#/
|
||||||
|
#/ \param soundBuffer Sound buffer object
|
||||||
|
#/
|
||||||
|
#/ \return Read-only pointer to the array of sound samples
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc sfSoundBuffer_getSamples*(soundBuffer: PSoundBuffer): ptr Int16{.
|
||||||
|
cdecl, importc: "sfSoundBuffer_getSamples", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the number of samples stored in a sound buffer
|
||||||
|
#/
|
||||||
|
#/ The array of samples can be accessed with the
|
||||||
|
#/ sfSoundBuffer_getSamples function.
|
||||||
|
proc getSampleCount*(soundBuffer: PSoundBuffer): cint{.
|
||||||
|
cdecl, importc: "sfSoundBuffer_getSampleCount", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the sample rate of a sound buffer
|
||||||
|
#/
|
||||||
|
#/ The sample rate is the number of samples played per second.
|
||||||
|
#/ The higher, the better the quality (for example, 44100
|
||||||
|
#/ samples/s is CD quality).
|
||||||
|
proc getSampleRate*(soundBuffer: PSoundBuffer): cuint{.
|
||||||
|
cdecl, importc: "sfSoundBuffer_getSampleRate", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the number of channels used by a sound buffer
|
||||||
|
#/
|
||||||
|
#/ If the sound is mono then the number of channels will
|
||||||
|
#/ be 1, 2 for stereo, etc.
|
||||||
|
proc getChannelCount*(soundBuffer: PSoundBuffer): cuint{.
|
||||||
|
cdecl, importc: "sfSoundBuffer_getChannelCount", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the total duration of a sound buffer
|
||||||
|
#/
|
||||||
|
#/ \param soundBuffer Sound buffer object
|
||||||
|
#/
|
||||||
|
#/ \return Sound duration
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getDuration*(soundBuffer: PSoundBuffer): TTime{.
|
||||||
|
cdecl, importc: "sfSoundBuffer_getDuration", dynlib: Lib.}
|
||||||
|
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Change the global volume of all the sounds and musics
|
||||||
|
#/
|
||||||
|
#/ The volume is a number between 0 and 100; it is combined with
|
||||||
|
#/ the individual volume of each sound / music.
|
||||||
|
#/ The default value for the volume is 100 (maximum).
|
||||||
|
#/
|
||||||
|
#/ \param volume New global volume, in the range [0, 100]
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc listenerSetGlobalVolume*(volume: cfloat){.
|
||||||
|
cdecl, importc: "sfListener_setGlobalVolume", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the current value of the global volume
|
||||||
|
#/
|
||||||
|
#/ \return Current global volume, in the range [0, 100]
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc listenerGetGlobalVolume*(): cfloat{.
|
||||||
|
cdecl, importc: "sfListener_getGlobalVolume", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the position of the listener in the scene
|
||||||
|
#/
|
||||||
|
#/ The default listener's position is (0, 0, 0).
|
||||||
|
#/
|
||||||
|
#/ \param position New position of the listener
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc listenerSetPosition*(position: TVector3f){.
|
||||||
|
cdecl, importc: "sfListener_setPosition", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the current position of the listener in the scene
|
||||||
|
#/
|
||||||
|
#/ \return The listener's position
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc listenerGetPosition*(): TVector3f{.
|
||||||
|
cdecl, importc: "sfListener_getPosition", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the orientation of the listener in the scene
|
||||||
|
#/
|
||||||
|
#/ The orientation defines the 3D axes of the listener
|
||||||
|
#/ (left, up, front) in the scene. The orientation vector
|
||||||
|
#/ doesn't have to be normalized.
|
||||||
|
#/ The default listener's orientation is (0, 0, -1).
|
||||||
|
#/
|
||||||
|
#/ \param position New direction of the listener
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc listenerSetDirection*(orientation: TVector3f){.
|
||||||
|
cdecl, importc: "sfListener_setDirection", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the current orientation of the listener in the scene
|
||||||
|
#/
|
||||||
|
#/ \return The listener's direction
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc listenerGetDirection*(): TVector3f{.
|
||||||
|
cdecl, importc: "sfListener_getDirection", dynlib: Lib.}
|
||||||
|
|
||||||
|
type
|
||||||
|
TSoundRecorderStartCallback* = proc (a2: pointer): bool {.cdecl.}
|
||||||
|
#/< Type of the callback used when starting a capture
|
||||||
|
TSoundRecorderProcessCallback* = proc(a2: ptr int16; a3: cuint;
|
||||||
|
a4: pointer): bool {.cdecl.}
|
||||||
|
#/< Type of the callback used to process audio data
|
||||||
|
TSoundRecorderStopCallback* = proc (a2: pointer){.cdecl.}
|
||||||
|
#/< Type of the callback used when stopping a capture
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Construct a new sound recorder from callback functions
|
||||||
|
#/
|
||||||
|
#/ \param onStart Callback function which will be called when a new capture starts (can be NULL)
|
||||||
|
#/ \param onProcess Callback function which will be called each time there's audio data to process
|
||||||
|
#/ \param onStop Callback function which will be called when the current capture stops (can be NULL)
|
||||||
|
#/ \param userData Data to pass to the callback function (can be NULL)
|
||||||
|
#/
|
||||||
|
#/ \return A new sfSoundRecorder object (NULL if failed)
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc newSoundRecorder*(onStart: TSoundRecorderStartCallback;
|
||||||
|
onProcess: TSoundRecorderProcessCallback;
|
||||||
|
onStop: TSoundRecorderStopCallback;
|
||||||
|
userData: pointer = nil): PSoundRecorder{.
|
||||||
|
cdecl, importc: "sfSoundRecorder_create", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Destroy a sound recorder
|
||||||
|
#/
|
||||||
|
#/ \param soundRecorder Sound recorder to destroy
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc destroy*(soundRecorder: PSoundRecorder){.
|
||||||
|
cdecl, importc: "sfSoundRecorder_destroy", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Start the capture of a sound recorder
|
||||||
|
#/
|
||||||
|
#/ The \a sampleRate parameter defines the number of audio samples
|
||||||
|
#/ captured per second. The higher, the better the quality
|
||||||
|
#/ (for example, 44100 samples/sec is CD quality).
|
||||||
|
#/ This function uses its own thread so that it doesn't block
|
||||||
|
#/ the rest of the program while the capture runs.
|
||||||
|
#/ Please note that only one capture can happen at the same time.
|
||||||
|
#/
|
||||||
|
#/ \param soundRecorder Sound recorder object
|
||||||
|
#/ \param sampleRate Desired capture rate, in number of samples per second
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc start*(soundRecorder: PSoundRecorder; sampleRate: cuint){.
|
||||||
|
cdecl, importc: "sfSoundRecorder_start", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Stop the capture of a sound recorder
|
||||||
|
#/
|
||||||
|
#/ \param soundRecorder Sound recorder object
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc stop*(soundRecorder: PSoundRecorder){.
|
||||||
|
cdecl, importc: "sfSoundRecorder_stop", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the sample rate of a sound recorder
|
||||||
|
#/
|
||||||
|
#/ The sample rate defines the number of audio samples
|
||||||
|
#/ captured per second. The higher, the better the quality
|
||||||
|
#/ (for example, 44100 samples/sec is CD quality).
|
||||||
|
#/
|
||||||
|
#/ \param soundRecorder Sound recorder object
|
||||||
|
#/
|
||||||
|
#/ \return Sample rate, in samples per second
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getSampleRate*(soundRecorder: PSoundRecorder): cuint{.
|
||||||
|
cdecl, importc: "sfSoundRecorder_getSampleRate", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Check if the system supports audio capture
|
||||||
|
#/
|
||||||
|
#/ This function should always be called before using
|
||||||
|
#/ the audio capture features. If it returns false, then
|
||||||
|
#/ any attempt to use sfSoundRecorder will fail.
|
||||||
|
#/
|
||||||
|
#/ \return sfTrue if audio capture is supported, sfFalse otherwise
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc soundRecorderIsAvailable*(): bool {.
|
||||||
|
cdecl, importc: "sfSoundRecorder_isAvailable", dynlib: Lib.}
|
||||||
|
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Create a new sound buffer recorder
|
||||||
|
#/
|
||||||
|
#/ \return A new sfSoundBufferRecorder object (NULL if failed)
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc newSoundBufferRecorder*(): PSoundBufferRecorder{.
|
||||||
|
cdecl, importc: "sfSoundBufferRecorder_create", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Destroy a sound buffer recorder
|
||||||
|
#/
|
||||||
|
#/ \param soundBufferRecorder Sound buffer recorder to destroy
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc destroy*(soundBufferRecorder: PSoundBufferRecorder){.
|
||||||
|
cdecl, importc: "sfSoundBufferRecorder_destroy", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Start the capture of a sound recorder recorder
|
||||||
|
#/
|
||||||
|
#/ The \a sampleRate parameter defines the number of audio samples
|
||||||
|
#/ captured per second. The higher, the better the quality
|
||||||
|
#/ (for example, 44100 samples/sec is CD quality).
|
||||||
|
#/ This function uses its own thread so that it doesn't block
|
||||||
|
#/ the rest of the program while the capture runs.
|
||||||
|
#/ Please note that only one capture can happen at the same time.
|
||||||
|
#/
|
||||||
|
#/ \param soundBufferRecorder Sound buffer recorder object
|
||||||
|
#/ \param sampleRate Desired capture rate, in number of samples per second
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc start*(soundBufferRecorder: PSoundBufferRecorder; sampleRate: cuint){.
|
||||||
|
cdecl, importc: "sfSoundBufferRecorder_start", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Stop the capture of a sound recorder
|
||||||
|
#/
|
||||||
|
#/ \param soundBufferRecorder Sound buffer recorder object
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc stop*(soundBufferRecorder: PSoundBufferRecorder){.
|
||||||
|
cdecl, importc: "sfSoundBufferRecorder_stop", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the sample rate of a sound buffer recorder
|
||||||
|
#/
|
||||||
|
#/ The sample rate defines the number of audio samples
|
||||||
|
#/ captured per second. The higher, the better the quality
|
||||||
|
#/ (for example, 44100 samples/sec is CD quality).
|
||||||
|
#/
|
||||||
|
#/ \param soundBufferRecorder Sound buffer recorder object
|
||||||
|
#/
|
||||||
|
#/ \return Sample rate, in samples per second
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getSampleRate*(soundBufferRecorder: PSoundBufferRecorder): cuint{.
|
||||||
|
cdecl, importc: "sfSoundBufferRecorder_getSampleRate", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the sound buffer containing the captured audio data
|
||||||
|
#/
|
||||||
|
#/ The sound buffer is valid only after the capture has ended.
|
||||||
|
#/ This function provides a read-only access to the internal
|
||||||
|
#/ sound buffer, but it can be copied if you need to
|
||||||
|
#/ make any modification to it.
|
||||||
|
#/
|
||||||
|
#/ \param soundBufferRecorder Sound buffer recorder object
|
||||||
|
#/
|
||||||
|
#/ \return Read-only access to the sound buffer
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getBuffer*(soundBufferRecorder: PSoundBufferRecorder): PSoundBuffer{.
|
||||||
|
cdecl, importc: "sfSoundBufferRecorder_getBuffer", dynlib: Lib.}
|
||||||
|
|
||||||
|
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief defines the data to fill by the OnGetData callback
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
type
|
||||||
|
PSoundStreamChunk* = ptr TSoundStreamChunk
|
||||||
|
TSoundStreamChunk*{.pure, final.} = object
|
||||||
|
samples*: ptr int16 #/< Pointer to the audio samples
|
||||||
|
sampleCount*: cuint #/< Number of samples pointed by Samples
|
||||||
|
|
||||||
|
TSoundStreamGetDataCallback* = proc (a2: PSoundStreamChunk;
|
||||||
|
a3: pointer): bool{.cdecl.}
|
||||||
|
#/< Type of the callback used to get a sound stream data
|
||||||
|
TSoundStreamSeekCallback* = proc (a2: TTime; a3: pointer){.cdecl.}
|
||||||
|
#/< Type of the callback used to seek in a sound stream
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Create a new sound stream
|
||||||
|
#/
|
||||||
|
#/ \param onGetData Function called when the stream needs more data (can't be NULL)
|
||||||
|
#/ \param onSeek Function called when the stream seeks (can't be NULL)
|
||||||
|
#/ \param channelCount Number of channels to use (1 = mono, 2 = stereo)
|
||||||
|
#/ \param sampleRate Sample rate of the sound (44100 = CD quality)
|
||||||
|
#/ \param userData Data to pass to the callback functions
|
||||||
|
#/
|
||||||
|
#/ \return A new sfSoundStream object
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc create*(onGetData: TSoundStreamGetDataCallback; onSeek: TSoundStreamSeekCallback;
|
||||||
|
channelCount: cuint; sampleRate: cuint; userData: pointer): PSoundStream{.
|
||||||
|
cdecl, importc: "sfSoundStream_create", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Destroy a sound stream
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream to destroy
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc destroy*(soundStream: PSoundStream){.
|
||||||
|
cdecl, importc: "sfSoundStream_destroy", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Start or resume playing a sound stream
|
||||||
|
#/
|
||||||
|
#/ This function starts the stream if it was stopped, resumes
|
||||||
|
#/ it if it was paused, and restarts it from beginning if it
|
||||||
|
#/ was it already playing.
|
||||||
|
#/ This function uses its own thread so that it doesn't block
|
||||||
|
#/ the rest of the program while the music is played.
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc play*(soundStream: PSoundStream){.
|
||||||
|
cdecl, importc: "sfSoundStream_play", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Pause a sound stream
|
||||||
|
#/
|
||||||
|
#/ This function pauses the stream if it was playing,
|
||||||
|
#/ otherwise (stream already paused or stopped) it has no effect.
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc pause*(soundStream: PSoundStream){.
|
||||||
|
cdecl, importc: "sfSoundStream_pause", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Stop playing a sound stream
|
||||||
|
#/
|
||||||
|
#/ This function stops the stream if it was playing or paused,
|
||||||
|
#/ and does nothing if it was already stopped.
|
||||||
|
#/ It also resets the playing position (unlike sfSoundStream_pause).
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc stop*(soundStream: PSoundStream){.
|
||||||
|
cdecl, importc: "sfSoundStream_stop", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the current status of a sound stream (stopped, paused, playing)
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#/ \return Current status
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getStatus*(soundStream: PSoundStream): TSoundStatus{.
|
||||||
|
cdecl, importc: "sfSoundStream_getStatus", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Return the number of channels of a sound stream
|
||||||
|
#/
|
||||||
|
#/ 1 channel means a mono sound, 2 means stereo, etc.
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#/ \return Number of channels
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getChannelCount*(soundStream: PSoundStream): cuint{.
|
||||||
|
cdecl, importc: "sfSoundStream_getChannelCount", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the sample rate of a sound stream
|
||||||
|
#/
|
||||||
|
#/ The sample rate is the number of audio samples played per
|
||||||
|
#/ second. The higher, the better the quality.
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#/ \return Sample rate, in number of samples per second
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getSampleRate*(soundStream: PSoundStream): cuint{.
|
||||||
|
cdecl, importc: "sfSoundStream_getSampleRate", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the pitch of a sound stream
|
||||||
|
#/
|
||||||
|
#/ The pitch represents the perceived fundamental frequency
|
||||||
|
#/ of a sound; thus you can make a stream more acute or grave
|
||||||
|
#/ by changing its pitch. A side effect of changing the pitch
|
||||||
|
#/ is to modify the playing speed of the stream as well.
|
||||||
|
#/ The default value for the pitch is 1.
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/ \param pitch New pitch to apply to the stream
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc setPitch*(soundStream: PSoundStream; pitch: cfloat){.
|
||||||
|
cdecl, importc: "sfSoundStream_setPitch", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the volume of a sound stream
|
||||||
|
#/
|
||||||
|
#/ The volume is a value between 0 (mute) and 100 (full volume).
|
||||||
|
#/ The default value for the volume is 100.
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/ \param volume Volume of the stream
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc setVolume*(soundStream: PSoundStream; volume: cfloat){.
|
||||||
|
cdecl, importc: "sfSoundStream_setVolume", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the 3D position of a sound stream in the audio scene
|
||||||
|
#/
|
||||||
|
#/ Only streams with one channel (mono streams) can be
|
||||||
|
#/ spatialized.
|
||||||
|
#/ The default position of a stream is (0, 0, 0).
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/ \param position Position of the stream in the scene
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc setPosition*(soundStream: PSoundStream; position: TVector3f){.
|
||||||
|
cdecl, importc: "sfSoundStream_setPosition", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Make a sound stream's position relative to the listener or absolute
|
||||||
|
#/
|
||||||
|
#/ Making a stream relative to the listener will ensure that it will always
|
||||||
|
#/ be played the same way regardless the position of the listener.
|
||||||
|
#/ This can be useful for non-spatialized streams, streams that are
|
||||||
|
#/ produced by the listener, or streams attached to it.
|
||||||
|
#/ The default value is false (position is absolute).
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/ \param relative sfTrue to set the position relative, sfFalse to set it absolute
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc setRelativeToListener*(soundStream: PSoundStream; relative: bool){.
|
||||||
|
cdecl, importc: "sfSoundStream_setRelativeToListener", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the minimum distance of a sound stream
|
||||||
|
#/
|
||||||
|
#/ The "minimum distance" of a stream is the maximum
|
||||||
|
#/ distance at which it is heard at its maximum volume. Further
|
||||||
|
#/ than the minimum distance, it will start to fade out according
|
||||||
|
#/ to its attenuation factor. A value of 0 ("inside the head
|
||||||
|
#/ of the listener") is an invalid value and is forbidden.
|
||||||
|
#/ The default value of the minimum distance is 1.
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/ \param distance New minimum distance of the stream
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc setMinDistance*(soundStream: PSoundStream; distance: cfloat){.
|
||||||
|
cdecl, importc: "sfSoundStream_setMinDistance", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set the attenuation factor of a sound stream
|
||||||
|
#/
|
||||||
|
#/ The attenuation is a multiplicative factor which makes
|
||||||
|
#/ the stream more or less loud according to its distance
|
||||||
|
#/ from the listener. An attenuation of 0 will produce a
|
||||||
|
#/ non-attenuated stream, i.e. its volume will always be the same
|
||||||
|
#/ whether it is heard from near or from far. On the other hand,
|
||||||
|
#/ an attenuation value such as 100 will make the stream fade out
|
||||||
|
#/ very quickly as it gets further from the listener.
|
||||||
|
#/ The default value of the attenuation is 1.
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/ \param attenuation New attenuation factor of the stream
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc setAttenuation*(soundStream: PSoundStream; attenuation: cfloat){.
|
||||||
|
cdecl, importc: "sfSoundStream_setAttenuation", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Change the current playing position of a sound stream
|
||||||
|
#/
|
||||||
|
#/ The playing position can be changed when the stream is
|
||||||
|
#/ either paused or playing.
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/ \param timeOffset New playing position
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc setPlayingOffset*(soundStream: PSoundStream; timeOffset: TTime){.
|
||||||
|
cdecl, importc: "sfSoundStream_setPlayingOffset", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Set whether or not a sound stream should loop after reaching the end
|
||||||
|
#/
|
||||||
|
#/ If set, the stream will restart from beginning after
|
||||||
|
#/ reaching the end and so on, until it is stopped or
|
||||||
|
#/ sfSoundStream_setLoop(stream, sfFalse) is called.
|
||||||
|
#/ The default looping state for sound streams is false.
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/ \param loop sfTrue to play in loop, sfFalse to play once
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc setLoop*(soundStream: PSoundStream; loop: bool){.
|
||||||
|
cdecl, importc: "sfSoundStream_setLoop", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the pitch of a sound stream
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#/ \return Pitch of the stream
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getPitch*(soundStream: PSoundStream): cfloat{.
|
||||||
|
cdecl, importc: "sfSoundStream_getPitch", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the volume of a sound stream
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#/ \return Volume of the stream, in the range [0, 100]
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getVolume*(soundStream: PSoundStream): cfloat{.
|
||||||
|
cdecl, importc: "sfSoundStream_getVolume", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the 3D position of a sound stream in the audio scene
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#/ \return Position of the stream in the world
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getPosition*(soundStream: PSoundStream): TVector3f{.
|
||||||
|
cdecl, importc: "sfSoundStream_getPosition", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Tell whether a sound stream's position is relative to the
|
||||||
|
#/ listener or is absolute
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#/ \return sfTrue if the position is relative, sfFalse if it's absolute
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc isRelativeToListener*(soundStream: PSoundStream): bool{.
|
||||||
|
cdecl, importc: "sfSoundStream_isRelativeToListener", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the minimum distance of a sound stream
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#/ \return Minimum distance of the stream
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getMinDistance*(soundStream: PSoundStream): cfloat{.
|
||||||
|
cdecl, importc: "sfSoundStream_getMinDistance", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the attenuation factor of a sound stream
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#/ \return Attenuation factor of the stream
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getAttenuation*(soundStream: PSoundStream): cfloat{.
|
||||||
|
cdecl, importc: "sfSoundStream_getAttenuation", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Tell whether or not a sound stream is in loop mode
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#/ \return sfTrue if the music is looping, sfFalse otherwise
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getLoop*(soundStream: PSoundStream): bool{.
|
||||||
|
cdecl, importc: "sfSoundStream_getLoop", dynlib: Lib.}
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
#/ \brief Get the current playing position of a sound stream
|
||||||
|
#/
|
||||||
|
#/ \param soundStream Sound stream object
|
||||||
|
#/
|
||||||
|
#/ \return Current playing position
|
||||||
|
#/
|
||||||
|
#//////////////////////////////////////////////////////////
|
||||||
|
proc getPlayingOffset*(soundStream: PSoundStream): TTime{.
|
||||||
|
cdecl, importc: "sfSoundStream_getPlayingOffset", dynlib: Lib.}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import sfml
|
||||||
|
{.deadCodeElim: on.}
|
||||||
|
let
|
||||||
|
Black*: TColor = color(0, 0, 0)
|
||||||
|
White*: TColor = color(255, 255, 255)
|
||||||
|
Red*: TColor = color(255, 0, 0)
|
||||||
|
Green*: TColor = color(0, 255, 0)
|
||||||
|
Blue*: TColor = color(0, 0, 255)
|
||||||
|
Yellow*: TColor = color(255, 255, 0)
|
||||||
|
Magenta*: TColor = color(255, 0, 255)
|
||||||
|
Cyan*: TColor = color(0, 255, 255)
|
||||||
|
Transparent*: TColor = color(0, 0, 0, 0)
|
||||||
|
Gray* = color(84, 84, 84)
|
||||||
|
RoyalBlue* = color(65, 105, 225)
|
||||||
|
##todo: define more colors lul
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
import sfml, math, strutils
|
||||||
|
{.deadCodeElim: on.}
|
||||||
228
tests/manyloc/keineschweine/enet_server/enet_client.nim
Normal file
228
tests/manyloc/keineschweine/enet_server/enet_client.nim
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
import enet, strutils,
|
||||||
|
sfml, sfml_colors, sg_gui, input_helpers,
|
||||||
|
math_helpers, sg_packets, estreams, tables,
|
||||||
|
json, sg_assets, client_helpers
|
||||||
|
if enetInit() != 0:
|
||||||
|
quit "Could not initialize ENet"
|
||||||
|
type
|
||||||
|
TClientSettings = object
|
||||||
|
resolution*: TVideoMode
|
||||||
|
offlineFile: string
|
||||||
|
dirserver: tuple[host: string, port: int16]
|
||||||
|
website*: string
|
||||||
|
var
|
||||||
|
clientSettings: TClientSettings
|
||||||
|
event: enet.TEvent
|
||||||
|
bConnected = false
|
||||||
|
runServer = true
|
||||||
|
gui = newGuiContainer()
|
||||||
|
zonelist = newGuiContainer()
|
||||||
|
kc = newKeyClient(setActive = true)
|
||||||
|
clock = newClock()
|
||||||
|
chatBox: PMessageArea
|
||||||
|
chatInput: PTextEntry
|
||||||
|
loginBtn, playBtn: PButton
|
||||||
|
fpsText = newText("", guiFont, 18)
|
||||||
|
connectionButtons: seq[PButton]
|
||||||
|
connectButton: PButton
|
||||||
|
u_alias, u_passwd: PTextEntry
|
||||||
|
dirServer: PServer
|
||||||
|
zone: PServer
|
||||||
|
showZoneList = false
|
||||||
|
myCreds = newScLogin(0, "", "") ##my session token
|
||||||
|
|
||||||
|
proc handleChat(server: PServer; buf: PBuffer) =
|
||||||
|
let msg = readScChat(buf)
|
||||||
|
chatBox.add msg
|
||||||
|
proc handlePlayerLogin(server: PServer; buf: PBuffer) =
|
||||||
|
let login = readScLogin(buf)
|
||||||
|
myCreds = login
|
||||||
|
echo("I am ", $myCreds)
|
||||||
|
|
||||||
|
|
||||||
|
kc.registerHandler MouseLeft, down, proc() =
|
||||||
|
gui.click(input_helpers.getMousePos())
|
||||||
|
|
||||||
|
block:
|
||||||
|
var pos = vec2f(15, 550)
|
||||||
|
chatBox = gui.newMessageArea(pos)
|
||||||
|
pos.y += 20
|
||||||
|
chatInput = gui.newTextEntry("...", pos, proc() =
|
||||||
|
sendPubChat dirServer, chatInput.getText()
|
||||||
|
chatInput.clearText())
|
||||||
|
|
||||||
|
gui.setActive(chatInput)
|
||||||
|
|
||||||
|
proc dispMessage(args: varargs[string, `$`]) =
|
||||||
|
var s = ""
|
||||||
|
for it in items(args):
|
||||||
|
s.add it
|
||||||
|
chatbox.add(s)
|
||||||
|
proc dispMessage(text: string) {.inline.} =
|
||||||
|
chatbox.add(text)
|
||||||
|
proc dispError(text: string) {.inline.} =
|
||||||
|
chatBox.add(newScChat(kind = CError, text = text))
|
||||||
|
|
||||||
|
proc updateButtons() =
|
||||||
|
let conn = dirServer.connected
|
||||||
|
for b in connectionButtons: setEnabled(b, conn)
|
||||||
|
if conn:
|
||||||
|
connectButton.setString "Disconnect"
|
||||||
|
else:
|
||||||
|
connectButton.setString "Connect"
|
||||||
|
|
||||||
|
proc poll(serv: PServer; timeout: cuint = 30) =
|
||||||
|
if serv.isNil or serv.host.isNil: return
|
||||||
|
if serv.connected:
|
||||||
|
while serv.host.hostService(event, timeout) > 0:
|
||||||
|
case event.kind
|
||||||
|
of EvtReceive:
|
||||||
|
var buf = newBuffer(event.packet)
|
||||||
|
|
||||||
|
serv.handlePackets(buf)
|
||||||
|
|
||||||
|
event.packet.destroy()
|
||||||
|
of EvtDisconnect:
|
||||||
|
dispMessage "Disconnected"
|
||||||
|
serv.connected = false
|
||||||
|
event.peer.data = nil
|
||||||
|
updateButtons()
|
||||||
|
of EvtNone: discard
|
||||||
|
else:
|
||||||
|
echo repr(event)
|
||||||
|
else:
|
||||||
|
if serv.host.hostService(event, timeout) > 0 and event.kind == EvtConnect:
|
||||||
|
dispMessage "Connected"
|
||||||
|
serv.connected = true
|
||||||
|
if serv.peer != event.peer:
|
||||||
|
serv.peer = event.peer
|
||||||
|
event.peer.data = serv
|
||||||
|
updateButtons()
|
||||||
|
|
||||||
|
proc tryLogin*(b: PButton) =
|
||||||
|
var login = newCsLogin(
|
||||||
|
alias = u_alias.getText(),
|
||||||
|
passwd = u_passwd.getText())
|
||||||
|
dirServer.send HLogin, login
|
||||||
|
proc tryTransition*(b: PButton) =
|
||||||
|
#zone.writePkt HZoneJoinReq, myCreds
|
||||||
|
proc tryConnect*(b: PButton) =
|
||||||
|
if not dirServer.connected:
|
||||||
|
var error: string
|
||||||
|
if not dirServer.connect(
|
||||||
|
clientSettings.dirServer.host,
|
||||||
|
clientSettings.dirServer.port,
|
||||||
|
error):
|
||||||
|
dispError(error)
|
||||||
|
else:
|
||||||
|
dirServer.peer.disconnect(1)
|
||||||
|
|
||||||
|
proc playOffline*(b: PButton) =
|
||||||
|
var errors: seq[string] = @[]
|
||||||
|
if loadSettingsFromFile(clientSettings.offlineFile, errors):
|
||||||
|
transition()
|
||||||
|
else:
|
||||||
|
dispMessage "Errors reading the file (", clientSettings.offlineFile, "):"
|
||||||
|
for e in errors: dispError(e)
|
||||||
|
|
||||||
|
proc getClientSettings*(): TClientSettings =
|
||||||
|
result = clientSettings
|
||||||
|
|
||||||
|
|
||||||
|
proc lobbyInit*() =
|
||||||
|
var s = json.parseFile("./client_settings.json")
|
||||||
|
clientSettings.offlineFile = "data/"
|
||||||
|
clientSettings.offlineFile.add s["default-file"].str
|
||||||
|
let dirserv = s["directory-server"]
|
||||||
|
clientSettings.dirserver.host = dirserv["host"].str
|
||||||
|
clientSettings.dirserver.port = dirserv["port"].num.int16
|
||||||
|
clientSettings.resolution.width = s["resolution"][0].num.cint
|
||||||
|
clientSettings.resolution.height= s["resolution"][1].num.cint
|
||||||
|
clientSettings.resolution.bitsPerPixel = s["resolution"][2].num.cint
|
||||||
|
clientSettings.website = s["website"].str
|
||||||
|
zonelist.setPosition(vec2f(200.0, 100.0))
|
||||||
|
connectionButtons = @[]
|
||||||
|
|
||||||
|
var pos = vec2f(10, 10)
|
||||||
|
u_alias = gui.newTextEntry(
|
||||||
|
if s.existsKey("alias"): s["alias"].str else: "alias",
|
||||||
|
pos)
|
||||||
|
pos.y += 20
|
||||||
|
u_passwd = gui.newTextEntry("buzz", pos)
|
||||||
|
pos.y += 20
|
||||||
|
connectionButtons.add(gui.newButton(
|
||||||
|
text = "Login",
|
||||||
|
position = pos,
|
||||||
|
onClick = tryLogin,
|
||||||
|
startEnabled = false))
|
||||||
|
pos.y += 20
|
||||||
|
fpsText.setPosition pos
|
||||||
|
pos.y += 20
|
||||||
|
connectButton = gui.newButton(
|
||||||
|
text = "Connect",
|
||||||
|
position = pos,
|
||||||
|
onClick = tryConnect)
|
||||||
|
pos.y += 20
|
||||||
|
gui.newButton("Test Files", position = pos, onClick = proc(b: PButton) =
|
||||||
|
var req = newCsZoneJoinReq(myCreds)
|
||||||
|
dirServer.send HZoneJoinReq, req)
|
||||||
|
pos.y += 20
|
||||||
|
connectionButtons.add(gui.newButton(
|
||||||
|
text = "Test Chat",
|
||||||
|
position = pos,
|
||||||
|
onClick = (proc(b: PButton) =
|
||||||
|
var pkt = newCsChat(text = "ohai")
|
||||||
|
dirServer.send HChat, pkt),
|
||||||
|
startEnabled = false))
|
||||||
|
pos.y += 20
|
||||||
|
downloadProgress.setPosition(pos)
|
||||||
|
downloadProgress.bg.setFillColor(color(34, 139, 34))
|
||||||
|
downloadProgress.bg.setSize(vec2f(0, 0))
|
||||||
|
gui.add(downloadProgress)
|
||||||
|
|
||||||
|
playBtn = gui.newButton(
|
||||||
|
text = "Play",
|
||||||
|
position = vec2f(680.0, 8.0),
|
||||||
|
onClick = tryTransition,
|
||||||
|
startEnabled = false)
|
||||||
|
gui.newButton(
|
||||||
|
text = "Play Offline",
|
||||||
|
position = vec2f(680.0, 28.0),
|
||||||
|
onClick = playOffline)
|
||||||
|
discard """gui.newButton(text = "Scrollback + 1", position = vec2f(185, 10), onClick = proc(b: PButton) =
|
||||||
|
messageArea.scrollBack += 1
|
||||||
|
update(messageArea))
|
||||||
|
gui.newButton(text = "Scrollback - 1", position = vec2f(185+160, 10), onClick = proc(b: PButton) =
|
||||||
|
messageArea.scrollBack -= 1
|
||||||
|
update(messageArea))
|
||||||
|
gui.newButton(text = "Flood msg area", position = vec2f(185, 30), onClick = proc(b: PButton) =
|
||||||
|
for i in 0.. <30:
|
||||||
|
dispMessage($i))"""
|
||||||
|
dirServer = newServer()
|
||||||
|
dirServer.addHandler HChat, handleChat
|
||||||
|
dirServer.addHandler HLogin, handlePlayerLogin
|
||||||
|
dirServer.addHandler HFileTransfer, client_helpers.handleFilePartRecv
|
||||||
|
dirServer.addHandler HChallengeResult, client_helpers.handleFileChallengeResult
|
||||||
|
dirServer.addHandler HFileChallenge, client_helpers.handleFileChallenge
|
||||||
|
|
||||||
|
proc lobbyReady*() =
|
||||||
|
kc.setActive()
|
||||||
|
gui.setActive(u_alias)
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
proc lobbyUpdate*(dt: float) =
|
||||||
|
dirServer.poll()
|
||||||
|
#let res = disp.poll()
|
||||||
|
gui.update(dt)
|
||||||
|
i = (i + 1) mod 60
|
||||||
|
if i == 0:
|
||||||
|
fpsText.setString("FPS: "& ff(1.0/dt))
|
||||||
|
|
||||||
|
proc lobbyDraw*(window: PRenderWindow) =
|
||||||
|
window.clear(Black)
|
||||||
|
window.draw chatBox
|
||||||
|
window.draw gui
|
||||||
|
window.draw fpsText
|
||||||
|
if showZonelist: window.draw zonelist
|
||||||
|
window.display()
|
||||||
|
|
||||||
294
tests/manyloc/keineschweine/enet_server/enet_server.nim
Normal file
294
tests/manyloc/keineschweine/enet_server/enet_server.nim
Normal file
|
|
@ -0,0 +1,294 @@
|
||||||
|
import enet, strutils, idgen, tables, math_helpers,
|
||||||
|
estreams, sg_packets, server_utils, sg_assets, client_helpers
|
||||||
|
when appType == "gui":
|
||||||
|
import sfml, sfml_colors, sg_gui,
|
||||||
|
input_helpers, sfml_stuff
|
||||||
|
else:
|
||||||
|
import times
|
||||||
|
type
|
||||||
|
TCallback = proc(client: PClient; buffer: PBuffer)
|
||||||
|
var
|
||||||
|
server: PHost
|
||||||
|
dirServer: PServer
|
||||||
|
standAloneMode = true
|
||||||
|
event: enet.TEvent
|
||||||
|
clientID = newIDGen[int32]()
|
||||||
|
clients = initTable[int32, PClient](64)
|
||||||
|
handlers = initTable[char, TCallback](32)
|
||||||
|
|
||||||
|
when appType == "gui":
|
||||||
|
var
|
||||||
|
gui = newGuiContainer()
|
||||||
|
chatBox = gui.newMessageArea(vec2f(15, 550))
|
||||||
|
window = newRenderWindow(videoMode(800, 600, 32), "Sup yo", sfDefaultSTyle)
|
||||||
|
mousepos = newText("", guiFont, 16)
|
||||||
|
fpsText = mousePos.copy()
|
||||||
|
inputClient = newKeyClient(setActive = true)
|
||||||
|
chatBox.sizeVisible = 30
|
||||||
|
mousePos.setColor(Green)
|
||||||
|
fpsText.setposition(vec2f(0, 20))
|
||||||
|
inputClient.registerHandler MouseLeft, down, proc() =
|
||||||
|
gui.click(input_helpers.getMousePos())
|
||||||
|
inputClient.registerHandler MouseMiddle, down, proc() =
|
||||||
|
let pos = input_helpers.getMousePos()
|
||||||
|
mousePos.setString("($1,$2)".format(ff(pos.x), ff(pos.y)))
|
||||||
|
mousePos.setPosition(pos)
|
||||||
|
proc dispMessage(args: varargs[string, `$`]) =
|
||||||
|
var s = ""
|
||||||
|
for it in items(args):
|
||||||
|
s.add it
|
||||||
|
chatbox.add(s)
|
||||||
|
proc dispError(args: varargs[string, `$`]) =
|
||||||
|
var s = ""
|
||||||
|
for it in items(args): s.add(it)
|
||||||
|
chatBox.add(newScChat(kind = CError, text = s))
|
||||||
|
else:
|
||||||
|
proc dispMessage(args: varargs[string, `$`]) =
|
||||||
|
var m = ""
|
||||||
|
for it in items(args): m.add(it)
|
||||||
|
echo "<msg> ", m
|
||||||
|
proc dispError(args: varargs[string, `$`]) =
|
||||||
|
var m = ""
|
||||||
|
for it in items(args): m.add(it)
|
||||||
|
echo "**", m
|
||||||
|
|
||||||
|
|
||||||
|
var pubChatQueue = newBuffer(1024)
|
||||||
|
proc queuePub(sender: PClient, msg: CsChat) =
|
||||||
|
var chat = newScChat(kind = CPub, fromPlayer = sender.alias, text = msg.text)
|
||||||
|
pubChatQueue.write(HChat)
|
||||||
|
pubChatQueue.pack(chat)
|
||||||
|
proc flushPubChat() =
|
||||||
|
if pubChatQueue.isDirty:
|
||||||
|
let packet = pubChatQueue.toPacket(FlagReliable)
|
||||||
|
for id, client in pairs(clients):
|
||||||
|
discard client.peer.send(0.cuchar, packet)
|
||||||
|
pubChatQueue.flush()
|
||||||
|
|
||||||
|
handlers[HChat] = proc(client: PClient; buffer: PBuffer) =
|
||||||
|
var chat = readCsChat(buffer)
|
||||||
|
|
||||||
|
if not client.auth:
|
||||||
|
client.sendError("You are not logged in.")
|
||||||
|
return
|
||||||
|
#if chat.target != "": ##private
|
||||||
|
# if alias2client.hasKey(chat.target):
|
||||||
|
# alias2client[chat.target].forwardPrivate(client, chat.text)
|
||||||
|
#else:
|
||||||
|
|
||||||
|
dispmessage("<", client.alias, "> ", chat.text)
|
||||||
|
queuePub(client, chat)
|
||||||
|
|
||||||
|
handlers[HLogin] = proc(client: PClient; buffer: PBuffer) =
|
||||||
|
var info = readCsLogin(buffer)
|
||||||
|
if client.auth:
|
||||||
|
client.sendError "You are already logged in."
|
||||||
|
return
|
||||||
|
client.alias = info.alias
|
||||||
|
client.auth = true
|
||||||
|
var resp = newScLogin(client.id, client.alias, "sessionkeylulz")
|
||||||
|
client.send HLogin, resp
|
||||||
|
client.sendMessage "welcome"
|
||||||
|
dispMessage("Client logged in: ", client)
|
||||||
|
|
||||||
|
|
||||||
|
handlers[HFileTransfer] = server_utils.handleFilePartAck
|
||||||
|
handlers[HFileChallenge] = server_utils.handleFileChallengeResp
|
||||||
|
|
||||||
|
handlers[HZoneJoinReq] = proc(client: PClient; buffer: PBuffer) =
|
||||||
|
var info = readCsZoneJoinReq(buffer)
|
||||||
|
dispmessage "Got zone join request"
|
||||||
|
client.startVerifyingFiles()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
import parseopt, matchers, os, json
|
||||||
|
|
||||||
|
|
||||||
|
if enetInit() != 0:
|
||||||
|
quit "Could not initialize ENet"
|
||||||
|
|
||||||
|
var address: enet.TAddress
|
||||||
|
|
||||||
|
block:
|
||||||
|
var zoneCfgFile = "./server_settings.json"
|
||||||
|
for kind, key, val in getOpt():
|
||||||
|
case kind
|
||||||
|
of cmdShortOption, cmdLongOption:
|
||||||
|
case key
|
||||||
|
of "f", "file":
|
||||||
|
if existsFile(val):
|
||||||
|
zoneCfgFile = val
|
||||||
|
else:
|
||||||
|
echo("File does not exist: ", val)
|
||||||
|
else:
|
||||||
|
echo("Unknown option: ", key," ", val)
|
||||||
|
else:
|
||||||
|
echo("Unknown option: ", key, " ", val)
|
||||||
|
var jsonSettings = parseFile(zoneCfgFile)
|
||||||
|
let
|
||||||
|
port = uint16(jsonSettings["port"].num)
|
||||||
|
zoneFile = jsonSettings["settings"].str
|
||||||
|
dirServerInfo = jsonSettings["dirserver"]
|
||||||
|
|
||||||
|
address.host = EnetHostAny
|
||||||
|
address.port = port
|
||||||
|
|
||||||
|
var path = getAppDir()/../"data"/zoneFile
|
||||||
|
if not existsFile(path):
|
||||||
|
echo("Zone settings file does not exist: ../data/", zoneFile)
|
||||||
|
echo(path)
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
discard """block:
|
||||||
|
var
|
||||||
|
TestFile: FileChallengePair
|
||||||
|
contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz")
|
||||||
|
testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32)
|
||||||
|
testFile.file = checksumStr(contents)
|
||||||
|
myAssets.add testFile"""
|
||||||
|
|
||||||
|
setCurrentDir getAppDir().parentDir()
|
||||||
|
let zonesettings = readFile(path)
|
||||||
|
var
|
||||||
|
errors: seq[string] = @[]
|
||||||
|
if not loadSettings(zoneSettings, errors):
|
||||||
|
echo("You have errors in your zone settings:")
|
||||||
|
for e in errors: echo("**", e)
|
||||||
|
quit(1)
|
||||||
|
errors.setLen 0
|
||||||
|
|
||||||
|
var pair: FileChallengePair
|
||||||
|
pair.challenge.file = zoneFile
|
||||||
|
pair.challenge.assetType = FZoneCfg
|
||||||
|
pair.challenge.fullLen = zoneSettings.len.int32
|
||||||
|
pair.file = checksumStr(zoneSettings)
|
||||||
|
myAssets.add pair
|
||||||
|
|
||||||
|
allAssets:
|
||||||
|
if not load(asset):
|
||||||
|
echo "Invalid or missing file ", file
|
||||||
|
else:
|
||||||
|
var pair: FileChallengePair
|
||||||
|
pair.challenge.file = file
|
||||||
|
pair.challenge.assetType = assetType
|
||||||
|
pair.challenge.fullLen = getFileSize(
|
||||||
|
expandPath(assetType, file)).int32
|
||||||
|
pair.file = asset.contents
|
||||||
|
myAssets.add pair
|
||||||
|
|
||||||
|
echo "Zone has ", myAssets.len, " associated assets"
|
||||||
|
|
||||||
|
dirServer = newServer()
|
||||||
|
|
||||||
|
dirServer.addHandler HDsMsg, proc(serv: PServer; buffer: PBuffer) =
|
||||||
|
var m = readDsMsg(buffer)
|
||||||
|
dispMessage("<DirServer> ", m.msg)
|
||||||
|
dirServer.addHandler HZoneLogin, proc(serv: PServer; buffer: PBuffer) =
|
||||||
|
let loggedIn = readDsZoneLogin(buffer).status
|
||||||
|
if loggedIn:
|
||||||
|
#dirServerConnected = true
|
||||||
|
|
||||||
|
if dirServerInfo.kind == JArray:
|
||||||
|
var error: string
|
||||||
|
if not dirServer.connect(dirServerInfo[0].str, dirServerInfo[1].num.int16, error):
|
||||||
|
dispError("<DirServer> "&error)
|
||||||
|
|
||||||
|
|
||||||
|
server = enet.createHost(address, 32, 2, 0, 0)
|
||||||
|
if server == nil:
|
||||||
|
quit "Could not create the server!"
|
||||||
|
|
||||||
|
dispMessage("Listening on port ", address.port)
|
||||||
|
|
||||||
|
var
|
||||||
|
serverRunning = true
|
||||||
|
when appType == "gui":
|
||||||
|
var frameRate = newClock()
|
||||||
|
var pubChatDelay = newClock()
|
||||||
|
else:
|
||||||
|
var frameRate = epochTime()
|
||||||
|
var pubChatDelay = frameRate
|
||||||
|
|
||||||
|
while serverRunning:
|
||||||
|
when appType == "gui":
|
||||||
|
let dt = frameRate.restart.asMilliseconds().float / 1000.0
|
||||||
|
|
||||||
|
for event in window.filterEvents():
|
||||||
|
case event.kind
|
||||||
|
of sfml.EvtClosed:
|
||||||
|
window.close()
|
||||||
|
serverRunning = false
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
else:
|
||||||
|
let dt = epochTime() - frameRate ##is this right? probably not
|
||||||
|
frameRate = epochTime()
|
||||||
|
|
||||||
|
while server.hostService(event, 10) > 0:
|
||||||
|
case event.kind
|
||||||
|
of EvtConnect:
|
||||||
|
var client = newClient()
|
||||||
|
clients[client.id] = client
|
||||||
|
|
||||||
|
event.peer.data = addr client.id
|
||||||
|
client.peer = event.peer
|
||||||
|
|
||||||
|
dispMessage("New client connected ", client)
|
||||||
|
|
||||||
|
var
|
||||||
|
msg = "hello"
|
||||||
|
resp = createPacket(cstring(msg), msg.len + 1, FlagReliable)
|
||||||
|
|
||||||
|
if event.peer.send(0.cuchar, resp) < 0:
|
||||||
|
echo "FAILED"
|
||||||
|
else:
|
||||||
|
echo "Replied"
|
||||||
|
of EvtReceive:
|
||||||
|
let client = clients[cast[ptr int32](event.peer.data)[]]
|
||||||
|
|
||||||
|
var buf = newBuffer(event.packet)
|
||||||
|
let k = buf.readChar()
|
||||||
|
if handlers.hasKey(k):
|
||||||
|
handlers[k](client, buf)
|
||||||
|
else:
|
||||||
|
dispError("Unknown packet from ", client)
|
||||||
|
|
||||||
|
destroy(event.packet)
|
||||||
|
of EvtDisconnect:
|
||||||
|
var
|
||||||
|
id = cast[ptr int32](event.peer.data)[]
|
||||||
|
client = clients[id]
|
||||||
|
if client.isNil:
|
||||||
|
disperror("CLIENT IS NIL!")
|
||||||
|
dispmessage(event.peer.data.isNil)
|
||||||
|
else:
|
||||||
|
dispMessage(clients[id], " disconnected")
|
||||||
|
GCUnref(clients[id])
|
||||||
|
clients.del id
|
||||||
|
|
||||||
|
event.peer.data = nil
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
when appType == "gui":
|
||||||
|
fpsText.setString(ff(1.0/dt))
|
||||||
|
if pubChatDelay.getElapsedTime.asSeconds > 0.25:
|
||||||
|
pubChatDelay.restart()
|
||||||
|
flushPubChat()
|
||||||
|
else:
|
||||||
|
pubChatDelay -= dt
|
||||||
|
if frameRate - pubChatDelay > 0.25:
|
||||||
|
flushPubChat()
|
||||||
|
|
||||||
|
when appType == "gui":
|
||||||
|
window.clear(Black)
|
||||||
|
window.draw(GUI)
|
||||||
|
window.draw chatbox
|
||||||
|
window.draw mousePos
|
||||||
|
window.draw fpstext
|
||||||
|
window.display()
|
||||||
|
|
||||||
|
server.destroy()
|
||||||
|
enetDeinit()
|
||||||
13
tests/manyloc/keineschweine/enet_server/nakefile.nim
Normal file
13
tests/manyloc/keineschweine/enet_server/nakefile.nim
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import nake
|
||||||
|
nakeimports
|
||||||
|
|
||||||
|
const
|
||||||
|
ServerDefines = "-d:NoSFML --forceBuild"
|
||||||
|
|
||||||
|
task "server", "build the server":
|
||||||
|
if shell("nimrod", ServerDefines, "-r", "compile", "enet_server") != 0:
|
||||||
|
quit "Failed to build"
|
||||||
|
task "gui", "build the server GUI mode":
|
||||||
|
if shell("nimrod", "--app:gui", ServerDefines, "-r", "compile", "enet_server") != 0:
|
||||||
|
quit "Failed to build"
|
||||||
|
|
||||||
9
tests/manyloc/keineschweine/enet_server/nimrod.cfg
Normal file
9
tests/manyloc/keineschweine/enet_server/nimrod.cfg
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
path = ".."
|
||||||
|
path = "../dependencies/sfml"
|
||||||
|
path = "../dependencies/enet"
|
||||||
|
path = "../dependencies/nake"
|
||||||
|
path = "../dependencies/genpacket"
|
||||||
|
path = "../lib"
|
||||||
|
define = "noChipmunk"
|
||||||
|
define = "noSFML"
|
||||||
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "Alpha Zone",
|
||||||
|
"desc": "Beta Testing",
|
||||||
|
"host": "localhost",
|
||||||
|
"port": 8024,
|
||||||
|
"settings": "alphazone.json",
|
||||||
|
"dirserver":["localhost",2049,"alphazone","skittles"]
|
||||||
|
}
|
||||||
120
tests/manyloc/keineschweine/enet_server/server_utils.nim
Normal file
120
tests/manyloc/keineschweine/enet_server/server_utils.nim
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
import enet, sg_packets, estreams, md5, zlib_helpers, client_helpers, strutils,
|
||||||
|
idgen, sg_assets, tables, os
|
||||||
|
type
|
||||||
|
PClient* = ref object
|
||||||
|
id*: int32
|
||||||
|
auth*: bool
|
||||||
|
alias*: string
|
||||||
|
peer*: PPeer
|
||||||
|
|
||||||
|
FileChallengePair* = tuple[challenge: ScFileChallenge; file: TChecksumFile]
|
||||||
|
PFileChallengeSequence* = ref TFileChallengeSequence
|
||||||
|
TFileChallengeSequence = object
|
||||||
|
index: int #which file is active
|
||||||
|
transfer: ScFileTransfer
|
||||||
|
file: ptr FileChallengePair
|
||||||
|
var
|
||||||
|
clientID = newIdGen[int32]()
|
||||||
|
myAssets*: seq[FileChallengePair] = @[]
|
||||||
|
fileChallenges = initTable[int32, PFileChallengeSequence](32)
|
||||||
|
const FileChunkSize = 256
|
||||||
|
|
||||||
|
proc free(client: PClient) =
|
||||||
|
if client.id != 0:
|
||||||
|
fileChallenges.del client.id
|
||||||
|
clientID.del client.id
|
||||||
|
proc newClient*(): PClient =
|
||||||
|
new(result, free)
|
||||||
|
result.id = clientID.next()
|
||||||
|
result.alias = "billy"
|
||||||
|
|
||||||
|
proc `$`*(client: PClient): string =
|
||||||
|
result = "$1:$2".format(client.id, client.alias)
|
||||||
|
|
||||||
|
proc send*[T](client: PClient; pktType: char; pkt: var T) =
|
||||||
|
var buf = newBuffer(128)
|
||||||
|
buf.write pktType
|
||||||
|
buf.pack pkt
|
||||||
|
discard client.peer.send(0.cuchar, buf, flagReliable)
|
||||||
|
|
||||||
|
proc sendMessage*(client: PClient; txt: string) =
|
||||||
|
var m = newScChat(CSystem, text = txt)
|
||||||
|
client.send HChat, m
|
||||||
|
proc sendError*(client: PClient; error: string) =
|
||||||
|
var m = newScChat(CError, text = error)
|
||||||
|
client.send HChat, m
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc next*(challenge: PFileChallengeSequence, client: PClient)
|
||||||
|
proc sendChunk*(challenge: PFileChallengeSequence, client: PClient)
|
||||||
|
|
||||||
|
proc startVerifyingFiles*(client: PClient) =
|
||||||
|
var fcs: PFileChallengeSequence
|
||||||
|
new(fcs)
|
||||||
|
fcs.index = -1
|
||||||
|
fileChallenges[client.id] = fcs
|
||||||
|
next(fcs, client)
|
||||||
|
|
||||||
|
proc next*(challenge: PFileChallengeSequence, client: PClient) =
|
||||||
|
inc(challenge.index)
|
||||||
|
if challenge.index >= myAssets.len:
|
||||||
|
client.sendMessage "You are cleared to enter"
|
||||||
|
fileChallenges.del client.id
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
echo myAssets.len, "assets"
|
||||||
|
challenge.file = addr myAssets[challenge.index]
|
||||||
|
client.send HFileChallenge, challenge.file.challenge # :rolleyes:
|
||||||
|
echo "sent challenge"
|
||||||
|
|
||||||
|
proc sendChunk*(challenge: PFileChallengeSequence, client: PClient) =
|
||||||
|
let size = min(FileChunkSize, challenge.transfer.fileSize - challenge.transfer.pos)
|
||||||
|
challenge.transfer.data.setLen size
|
||||||
|
copyMem(
|
||||||
|
addr challenge.transfer.data[0],
|
||||||
|
addr challenge.file.file.compressed[challenge.transfer.pos],
|
||||||
|
size)
|
||||||
|
client.send HFileTransfer, challenge.transfer
|
||||||
|
echo "chunk sent"
|
||||||
|
|
||||||
|
proc startSend*(challenge: PFileChallengeSequence, client: PClient) =
|
||||||
|
challenge.transfer.fileSize = challenge.file.file.compressed.len().int32
|
||||||
|
challenge.transfer.pos = 0
|
||||||
|
challenge.transfer.data = ""
|
||||||
|
challenge.transfer.data.setLen FileChunkSize
|
||||||
|
challenge.sendChunk(client)
|
||||||
|
echo "starting xfer"
|
||||||
|
|
||||||
|
## HFileTransfer
|
||||||
|
proc handleFilePartAck*(client: PClient; buffer: PBuffer) =
|
||||||
|
echo "got filepartack"
|
||||||
|
var
|
||||||
|
ftrans = readCsFilepartAck(buffer)
|
||||||
|
fcSeq = fileChallenges[client.id]
|
||||||
|
fcSeq.transfer.pos = ftrans.lastPos
|
||||||
|
fcSeq.sendChunk client
|
||||||
|
|
||||||
|
## HFileCHallenge
|
||||||
|
proc handleFileChallengeResp*(client: PClient; buffer: PBuffer) =
|
||||||
|
echo "got file challenge resp"
|
||||||
|
var
|
||||||
|
fcResp = readCsFileChallenge(buffer)
|
||||||
|
fcSeq = fileChallenges[client.id]
|
||||||
|
let index = $(fcSeq.index + 1) / $(myAssets.len)
|
||||||
|
if fcResp.needFile:
|
||||||
|
client.sendMessage "Sending file... "&index
|
||||||
|
fcSeq.startSend(client)
|
||||||
|
else:
|
||||||
|
var resp = newScChallengeResult(false)
|
||||||
|
if fcResp.checksum == fcSeq.file.file.sum: ##client is good
|
||||||
|
client.sendMessage "Checksum is good. "&index
|
||||||
|
resp.status = true
|
||||||
|
client.send HChallengeResult, resp
|
||||||
|
fcSeq.next(client)
|
||||||
|
else:
|
||||||
|
client.sendMessage "Checksum is bad, sending file... "&index
|
||||||
|
client.send HChallengeResult, resp
|
||||||
|
fcSeq.startSend(client)
|
||||||
|
|
||||||
725
tests/manyloc/keineschweine/keineschweine.nim
Normal file
725
tests/manyloc/keineschweine/keineschweine.nim
Normal file
|
|
@ -0,0 +1,725 @@
|
||||||
|
import
|
||||||
|
os, math, strutils, gl, tables,
|
||||||
|
sfml, sfml_audio, sfml_colors, chipmunk, math_helpers,
|
||||||
|
input_helpers, animations, game_objects, sfml_stuff, map_filter,
|
||||||
|
sg_gui, sg_assets, sound_buffer, enet_client
|
||||||
|
when defined(profiler):
|
||||||
|
import nimprof
|
||||||
|
{.deadCodeElim: on.}
|
||||||
|
type
|
||||||
|
PPlayer* = ref TPlayer
|
||||||
|
TPlayer* = object
|
||||||
|
id: uint16
|
||||||
|
vehicle: PVehicle
|
||||||
|
spectator: bool
|
||||||
|
alias: string
|
||||||
|
nameTag: PText
|
||||||
|
items: seq[PItem]
|
||||||
|
PVehicle* = ref TVehicle
|
||||||
|
TVehicle* = object
|
||||||
|
body*: chipmunk.PBody
|
||||||
|
shape*: chipmunk.PShape
|
||||||
|
record*: PVehicleRecord
|
||||||
|
sprite*: PSprite
|
||||||
|
spriteRect*: TIntRect
|
||||||
|
occupant: PPlayer
|
||||||
|
when false:
|
||||||
|
position*: TVector2f
|
||||||
|
velocity*: TVector2f
|
||||||
|
angle*: float
|
||||||
|
PItem* = ref object
|
||||||
|
record: PItemRecord
|
||||||
|
cooldown: float
|
||||||
|
PLiveBullet* = ref TLiveBullet ##represents a live bullet in the arena
|
||||||
|
TLiveBullet* = object
|
||||||
|
lifetime*: float
|
||||||
|
dead: bool
|
||||||
|
anim*: PAnimation
|
||||||
|
record*: PBulletRecord
|
||||||
|
fromPlayer*: PPlayer
|
||||||
|
trailDelay*: float
|
||||||
|
body: chipmunk.PBody
|
||||||
|
shape: chipmunk.PShape
|
||||||
|
import vehicles
|
||||||
|
const
|
||||||
|
LGrabbable* = (1 shl 0).TLayers
|
||||||
|
LBorders* = (1 shl 1).TLayers
|
||||||
|
LPlayer* = ((1 shl 2) and LBorders.int).TLayers
|
||||||
|
LEnemy* = ((1 shl 4) and LBorders.int).TLayers
|
||||||
|
LEnemyFire* = (LPlayer).TLayers
|
||||||
|
LPlayerFire* = (LEnemy).TLayers
|
||||||
|
CTBullet = 1.TCollisionType
|
||||||
|
CTVehicle= 2.TCollisionType
|
||||||
|
##temporary constants
|
||||||
|
W_LIMIT = 2.3
|
||||||
|
V_LIMIT = 35
|
||||||
|
MaxLocalBots = 3
|
||||||
|
var
|
||||||
|
localPlayer: PPlayer
|
||||||
|
localBots: seq[PPlayer] = @[]
|
||||||
|
activeVehicle: PVehicle
|
||||||
|
myVehicles: seq[PVehicle] = @[]
|
||||||
|
objects: seq[PGameObject] = @[]
|
||||||
|
liveBullets: seq[PLiveBullet] = @[]
|
||||||
|
explosions: seq[PAnimation] = @[]
|
||||||
|
gameRunning = true
|
||||||
|
frameRate = newClock()
|
||||||
|
showStars = off
|
||||||
|
levelArea: TIntRect
|
||||||
|
videoMode: TVideoMode
|
||||||
|
window: PRenderWindow
|
||||||
|
worldView: PView
|
||||||
|
guiView: PView
|
||||||
|
space = newSpace()
|
||||||
|
ingameClient = newKeyClient("ingame")
|
||||||
|
specInputClient = newKeyClient("spec")
|
||||||
|
specGui = newGuiContainer()
|
||||||
|
stars: seq[PSpriteSheet] = @[]
|
||||||
|
playBtn: PButton
|
||||||
|
shipSelect = newGuiContainer()
|
||||||
|
delObjects: seq[int] = @[]
|
||||||
|
showShipSelect = false
|
||||||
|
myPosition: array[0..1, TVector3f] ##for audio positioning
|
||||||
|
let
|
||||||
|
nameTagOffset = vec2f(0.0, 1.0)
|
||||||
|
when defined(escapeMenuTest):
|
||||||
|
import browsers
|
||||||
|
var
|
||||||
|
escMenu = newGuiContainer(vec2f(100, 100))
|
||||||
|
escMenuOpen = false
|
||||||
|
pos = vec2f(0, 0)
|
||||||
|
escMenu.newButton("Some Website", pos, proc(b: PButton) =
|
||||||
|
openDefaultBrowser(getClientSettings().website))
|
||||||
|
pos.y += 20.0
|
||||||
|
escMenu.newButton("Back to Lobby", pos, proc(b: PButton) =
|
||||||
|
echo "herro")
|
||||||
|
proc toggleEscape() =
|
||||||
|
escMenuOpen = not escMenuOpen
|
||||||
|
ingameClient.registerHandler(KeyEscape, down, toggleEscape)
|
||||||
|
specInputClient.registerHandler(KeyEscape, down, toggleEscape)
|
||||||
|
when defined(foo):
|
||||||
|
var mouseSprite: sfml.PCircleShape
|
||||||
|
when defined(recordMode):
|
||||||
|
var
|
||||||
|
snapshots: seq[PImage] = @[]
|
||||||
|
isRecording = false
|
||||||
|
proc startRecording() =
|
||||||
|
if snapshots.len > 100: return
|
||||||
|
echo "Started recording"
|
||||||
|
isRecording = true
|
||||||
|
proc stopRecording() =
|
||||||
|
if isRecording:
|
||||||
|
echo "Stopped recording. ", snapshots.len, " images."
|
||||||
|
isRecording = false
|
||||||
|
proc zeroPad*(s: string; minLen: int): string =
|
||||||
|
if s.len < minLen:
|
||||||
|
result = repeatChar(minLen - s.len, '0')
|
||||||
|
result.add s
|
||||||
|
else:
|
||||||
|
result = s
|
||||||
|
var
|
||||||
|
recordButton = newButton(
|
||||||
|
nil, text = "Record", position = vec2f(680, 50),
|
||||||
|
onClick = proc(b: PButton) = startRecording())
|
||||||
|
|
||||||
|
proc newNameTag*(text: string): PText =
|
||||||
|
result = newText()
|
||||||
|
result.setFont(guiFont)
|
||||||
|
result.setCharacterSize(14)
|
||||||
|
result.setColor(Red)
|
||||||
|
result.setString(text)
|
||||||
|
|
||||||
|
var debugText = newNameTag("Loading...")
|
||||||
|
debugText.setPosition(vec2f(0.0, 600.0 - 50.0))
|
||||||
|
|
||||||
|
when defined(showFPS):
|
||||||
|
var fpsText = newNameTag("0")
|
||||||
|
#fpsText.setCharacterSize(16)
|
||||||
|
fpsText.setPosition(vec2f(300.0, (800 - 50).float))
|
||||||
|
|
||||||
|
proc mouseToSpace*(): TVector =
|
||||||
|
result = window.convertCoords(vec2i(getMousePos()), worldView).sfml2cp()
|
||||||
|
|
||||||
|
proc explode*(b: PLiveBullet)
|
||||||
|
## TCollisionBeginFunc
|
||||||
|
proc collisionBulletPlayer(arb: PArbiter; space: PSpace;
|
||||||
|
data: pointer): Bool{.cdecl.} =
|
||||||
|
var
|
||||||
|
bullet = cast[PLiveBullet](arb.a.data)
|
||||||
|
target = cast[PVehicle](arb.b.data)
|
||||||
|
if target.occupant.isNil or target.occupant == bullet.fromPlayer: return
|
||||||
|
bullet.explode()
|
||||||
|
|
||||||
|
proc angularDampingSim(body: PBody, gravity: TVector, damping, dt: CpFloat){.cdecl.} =
|
||||||
|
body.w -= (body.w * 0.98 * dt)
|
||||||
|
body.updateVelocity(gravity, damping, dt)
|
||||||
|
|
||||||
|
proc initLevel() =
|
||||||
|
loadAllAssets()
|
||||||
|
|
||||||
|
if not space.isNil: space.destroy()
|
||||||
|
space = newSpace()
|
||||||
|
space.addCollisionHandler CTBullet, CTVehicle, collisionBulletPlayer,
|
||||||
|
nil, nil, nil, nil
|
||||||
|
|
||||||
|
let levelSettings = getLevelSettings()
|
||||||
|
levelArea.width = levelSettings.size.x
|
||||||
|
levelArea.height= levelSettings.size.y
|
||||||
|
let borderSeq = @[
|
||||||
|
vector(0, 0), vector(levelArea.width.float, 0.0),
|
||||||
|
vector(levelArea.width.float, levelArea.height.float), vector(0.0, levelArea.height.float)]
|
||||||
|
for i in 0..3:
|
||||||
|
var seg = space.addShape(
|
||||||
|
newSegmentShape(
|
||||||
|
space.staticBody,
|
||||||
|
borderSeq[i],
|
||||||
|
borderSeq[(i + 1) mod 4],
|
||||||
|
8.0))
|
||||||
|
seg.setElasticity 0.96
|
||||||
|
seg.setLayers(LBorders)
|
||||||
|
if levelSettings.starfield.len > 0:
|
||||||
|
showStars = true
|
||||||
|
for sprite in levelSettings.starfield:
|
||||||
|
sprite.tex.setRepeated(true)
|
||||||
|
sprite.sprite.setTextureRect(levelArea)
|
||||||
|
sprite.sprite.setOrigin(vec2f(0, 0))
|
||||||
|
stars.add(sprite)
|
||||||
|
var pos = vec2f(0.0, 0.0)
|
||||||
|
for veh in playableVehicles():
|
||||||
|
shipSelect.newButton(
|
||||||
|
veh.name,
|
||||||
|
position = pos,
|
||||||
|
onClick = proc(b: PButton) =
|
||||||
|
echo "-__-")
|
||||||
|
pos.y += 18.0
|
||||||
|
|
||||||
|
|
||||||
|
proc newItem*(record: PItemRecord): PItem =
|
||||||
|
new(result)
|
||||||
|
result.record = record
|
||||||
|
proc newItem*(name: string): PItem {.inline.} =
|
||||||
|
return newItem(fetchItm(name))
|
||||||
|
proc canUse*(itm: PItem): bool =
|
||||||
|
if itm.cooldown > 0.0: return
|
||||||
|
return true
|
||||||
|
proc update*(itm: PItem; dt: float) =
|
||||||
|
if itm.cooldown > 0:
|
||||||
|
itm.cooldown -= dt
|
||||||
|
|
||||||
|
proc free(obj: PLiveBullet) =
|
||||||
|
obj.shape.free
|
||||||
|
obj.body.free
|
||||||
|
obj.record = nil
|
||||||
|
|
||||||
|
|
||||||
|
template newExplosion(obj, animation): stmt =
|
||||||
|
explosions.add(newAnimation(animation, AnimOnce, obj.body.getPos.cp2sfml, obj.body.getAngle))
|
||||||
|
template newExplosion(obj, animation, angle): stmt =
|
||||||
|
explosions.add(newAnimation(animation, AnimOnce, obj.body.getPos.cp2sfml, angle))
|
||||||
|
|
||||||
|
proc explode*(b: PLiveBullet) =
|
||||||
|
if b.dead: return
|
||||||
|
b.dead = true
|
||||||
|
space.removeShape b.shape
|
||||||
|
space.removeBody b.body
|
||||||
|
if not b.record.explosion.anim.isNil:
|
||||||
|
newExplosion(b, b.record.explosion.anim)
|
||||||
|
playSound(b.record.explosion.sound, b.body.getPos())
|
||||||
|
|
||||||
|
proc bulletUpdate(body: PBody, gravity: TVector, damping, dt: CpFloat){.cdecl.} =
|
||||||
|
body.updateVelocity(gravity, damping, dt)
|
||||||
|
|
||||||
|
template getPhysical() {.immediate.} =
|
||||||
|
result.body = space.addBody(newBody(
|
||||||
|
record.physics.mass,
|
||||||
|
record.physics.moment))
|
||||||
|
result.shape = space.addShape(
|
||||||
|
chipmunk.newCircleShape(
|
||||||
|
result.body,
|
||||||
|
record.physics.radius,
|
||||||
|
vectorZero))
|
||||||
|
|
||||||
|
proc newBullet*(record: PBulletRecord; fromPlayer: PPlayer): PLiveBullet =
|
||||||
|
new(result, free)
|
||||||
|
result.anim = newAnimation(record.anim, AnimLoop)
|
||||||
|
result.fromPlayer = fromPlayer
|
||||||
|
result.lifetime = record.lifetime
|
||||||
|
result.record = record
|
||||||
|
getPhysical()
|
||||||
|
if fromPlayer == localPlayer:
|
||||||
|
result.shape.setLayers(LPlayerFire)
|
||||||
|
else:
|
||||||
|
result.shape.setLayers(LEnemyFire)
|
||||||
|
result.shape.setCollisionType CTBullet
|
||||||
|
result.shape.setUserData(cast[ptr TLiveBullet](result))
|
||||||
|
let
|
||||||
|
fireAngle = fromPlayer.vehicle.body.getAngle()
|
||||||
|
fireAngleV = vectorForAngle(fireAngle)
|
||||||
|
result.body.setAngle fireAngle
|
||||||
|
result.body.setPos(fromPlayer.vehicle.body.getPos() + (fireAngleV * fromPlayer.vehicle.shape.getCircleRadius()))
|
||||||
|
#result.body.velocityFunc = bulletUpdate
|
||||||
|
result.body.setVel((fromPlayer.vehicle.body.getVel() * record.inheritVelocity) + (fireAngleV * record.baseVelocity))
|
||||||
|
|
||||||
|
proc update*(b: PLiveBullet; dt: float): bool =
|
||||||
|
if b.dead: return true
|
||||||
|
b.lifetime -= dt
|
||||||
|
b.anim.next(dt)
|
||||||
|
#b.anim.sprite.setPosition(b.body.getPos.floor())
|
||||||
|
b.anim.setPos(b.body.getPos)
|
||||||
|
b.anim.setAngle(b.body.getAngle())
|
||||||
|
if b.lifetime <= 0.0:
|
||||||
|
b.explode()
|
||||||
|
return true
|
||||||
|
b.trailDelay -= dt
|
||||||
|
if b.trailDelay <= 0.0:
|
||||||
|
b.trailDelay += b.record.trail.timer
|
||||||
|
if b.record.trail.anim.isNil: return
|
||||||
|
newExplosion(b, b.record.trail.anim)
|
||||||
|
proc draw*(window: PRenderWindow; b: PLiveBullet) {.inline.} =
|
||||||
|
draw(window, b.anim.sprite)
|
||||||
|
|
||||||
|
|
||||||
|
proc free*(veh: PVehicle) =
|
||||||
|
("Destroying vehicle "& veh.record.name).echo
|
||||||
|
destroy(veh.sprite)
|
||||||
|
if veh.shape.isNil: "Free'd vehicle's shape was NIL!".echo
|
||||||
|
else: space.removeShape(veh.shape)
|
||||||
|
if veh.body.isNil: "Free'd vehicle's BODY was NIL!".echo
|
||||||
|
else: space.removeBody(veh.body)
|
||||||
|
veh.body.free()
|
||||||
|
veh.shape.free()
|
||||||
|
veh.sprite = nil
|
||||||
|
veh.body = nil
|
||||||
|
veh.shape = nil
|
||||||
|
|
||||||
|
|
||||||
|
proc newVehicle*(record: PVehicleRecord): PVehicle =
|
||||||
|
echo("Creating "& record.name)
|
||||||
|
new(result, free)
|
||||||
|
result.record = record
|
||||||
|
result.sprite = result.record.anim.spriteSheet.sprite.copy()
|
||||||
|
result.spriteRect = result.sprite.getTextureRect()
|
||||||
|
getPhysical()
|
||||||
|
result.body.setAngVelLimit W_LIMIT
|
||||||
|
result.body.setVelLimit result.record.handling.topSpeed
|
||||||
|
result.body.velocityFunc = angularDampingSim
|
||||||
|
result.shape.setCollisionType CTVehicle
|
||||||
|
result.shape.setUserData(cast[ptr TVehicle](result))
|
||||||
|
proc newVehicle*(name: string): PVehicle =
|
||||||
|
result = newVehicle(fetchVeh(name))
|
||||||
|
|
||||||
|
proc update*(obj: PVehicle) =
|
||||||
|
obj.sprite.setPosition(obj.body.getPos.floor)
|
||||||
|
obj.spriteRect.left = (((-obj.body.getAngVel + W_LIMIT) / (W_LIMIT*2.0) * (obj.record.anim.spriteSheet.cols - 1).float).floor.int * obj.record.anim.spriteSheet.framew).cint
|
||||||
|
obj.spriteRect.top = ((obj.offsetAngle.wmod(TAU) / TAU) * obj.record.anim.spriteSheet.rows.float).floor.cint * obj.record.anim.spriteSheet.frameh.cint
|
||||||
|
obj.sprite.setTextureRect(obj.spriteRect)
|
||||||
|
|
||||||
|
|
||||||
|
proc newPlayer*(alias: string = "poo"): PPlayer =
|
||||||
|
new(result)
|
||||||
|
result.spectator = true
|
||||||
|
result.alias = alias
|
||||||
|
result.nameTag = newNameTag(result.alias)
|
||||||
|
result.items = @[]
|
||||||
|
proc updateItems*(player: PPlayer, dt: float) =
|
||||||
|
for i in items(player.items):
|
||||||
|
update(i, dt)
|
||||||
|
proc addItem*(player: PPlayer; name: string) =
|
||||||
|
player.items.add newItem(name)
|
||||||
|
proc useItem*(player: PPlayer; slot: int) =
|
||||||
|
if slot > player.items.len - 1: return
|
||||||
|
let item = player.items[slot]
|
||||||
|
if item.canUse:
|
||||||
|
item.cooldown += item.record.cooldown
|
||||||
|
let b = newBullet(item.record.bullet, player)
|
||||||
|
liveBullets.add(b)
|
||||||
|
sound_buffer.playSound(item.record.useSound, b.body.getPos)
|
||||||
|
|
||||||
|
proc update*(obj: PPlayer) =
|
||||||
|
if not obj.spectator:
|
||||||
|
obj.vehicle.update()
|
||||||
|
obj.nameTag.setPosition(obj.vehicle.body.getPos.floor + (nameTagOffset * (obj.vehicle.record.physics.radius + 5).cfloat))
|
||||||
|
|
||||||
|
proc draw(window: PRenderWindow, player: PPlayer) {.inline.} =
|
||||||
|
if not player.spectator:
|
||||||
|
if player.vehicle != nil:
|
||||||
|
window.draw(player.vehicle.sprite)
|
||||||
|
window.draw(player.nameTag)
|
||||||
|
|
||||||
|
proc setVehicle(p: PPlayer; v: PVehicle) =
|
||||||
|
p.vehicle = v #sorry mom, this is just how things worked out ;(
|
||||||
|
if not v.isNil:
|
||||||
|
v.occupant = p
|
||||||
|
|
||||||
|
proc createBot() =
|
||||||
|
if localBots.len < MaxLocalBots:
|
||||||
|
var bot = newPlayer("Dodo Brown")
|
||||||
|
bot.setVehicle(newVehicle("Turret0"))
|
||||||
|
if bot.isNil:
|
||||||
|
echo "BOT IS NIL"
|
||||||
|
return
|
||||||
|
elif bot.vehicle.isNil:
|
||||||
|
echo "BOT VEH IS NIL"
|
||||||
|
return
|
||||||
|
localBots.add(bot)
|
||||||
|
bot.vehicle.body.setPos(vector(100, 100))
|
||||||
|
echo "new bot at ", $bot.vehicle.body.getPos()
|
||||||
|
|
||||||
|
var inputCursor = newVertexArray(sfml.Lines, 2)
|
||||||
|
inputCursor[0].position = vec2f(10.0, 10.0)
|
||||||
|
inputCursor[1].position = vec2f(50.0, 90.0)
|
||||||
|
|
||||||
|
proc hasVehicle(p: PPlayer): bool {.inline.} =
|
||||||
|
result = not p.spectator and not p.vehicle.isNil
|
||||||
|
|
||||||
|
proc setMyVehicle(v: PVehicle) {.inline.} =
|
||||||
|
activeVehicle = v
|
||||||
|
localPlayer.setVehicle v
|
||||||
|
|
||||||
|
proc unspec() =
|
||||||
|
var veh = newVehicle("Turret0")
|
||||||
|
if not veh.isNil:
|
||||||
|
setMyVehicle veh
|
||||||
|
localPlayer.spectator = false
|
||||||
|
ingameClient.setActive
|
||||||
|
veh.body.setPos vector(100, 100)
|
||||||
|
veh.shape.setLayers(LPlayer)
|
||||||
|
when defined(debugWeps):
|
||||||
|
localPlayer.addItem("Mass Driver")
|
||||||
|
localPlayer.addItem("Neutron Bomb")
|
||||||
|
localPlayer.additem("Dem Lasers")
|
||||||
|
localPlayer.addItem("Mold Spore Beam")
|
||||||
|
localPlayer.addItem("Genericorp Mine")
|
||||||
|
localPlayer.addItem("Gravitic Bomb")
|
||||||
|
proc spec() =
|
||||||
|
setMyVehicle nil
|
||||||
|
localPlayer.spectator = true
|
||||||
|
specInputClient.setActive
|
||||||
|
|
||||||
|
var
|
||||||
|
specLimiter = newClock()
|
||||||
|
timeBetweenSpeccing = 1.0 #seconds
|
||||||
|
proc toggleSpec() {.inline.} =
|
||||||
|
if specLimiter.getElapsedTime.asSeconds < timeBetweenSpeccing:
|
||||||
|
return
|
||||||
|
specLimiter.restart()
|
||||||
|
if localPlayer.isNil:
|
||||||
|
echo("OMG WTF PLAYER IS NILL!!")
|
||||||
|
elif localPlayer.spectator: unspec()
|
||||||
|
else: spec()
|
||||||
|
|
||||||
|
proc addObject*(name: string) =
|
||||||
|
var o = newObject(name)
|
||||||
|
if not o.isNil:
|
||||||
|
echo "Adding object ", o
|
||||||
|
discard space.addBody(o.body)
|
||||||
|
discard space.addShape(o.shape)
|
||||||
|
o.shape.setLayers(LGrabbable)
|
||||||
|
objects.add(o)
|
||||||
|
proc explode(obj: PGameObject) =
|
||||||
|
echo obj, " exploded"
|
||||||
|
let ind = objects.find(obj)
|
||||||
|
if ind != -1:
|
||||||
|
delObjects.add ind
|
||||||
|
proc update(obj: PGameObject; dt: float) =
|
||||||
|
if not(obj.anim.next(dt)):
|
||||||
|
obj.explode()
|
||||||
|
else:
|
||||||
|
obj.anim.setPos(obj.body.getPos)
|
||||||
|
obj.anim.setAngle(obj.body.getAngle)
|
||||||
|
|
||||||
|
proc toggleShipSelect() =
|
||||||
|
showShipSelect = not showShipSelect
|
||||||
|
proc handleLClick() =
|
||||||
|
let pos = input_helpers.getMousePos()
|
||||||
|
when defined(escapeMenuTest):
|
||||||
|
if escMenuOpen:
|
||||||
|
escMenu.click(pos)
|
||||||
|
return
|
||||||
|
if showShipSelect:
|
||||||
|
shipSelect.click(pos)
|
||||||
|
if localPlayer.spectator:
|
||||||
|
specGui.click(pos)
|
||||||
|
|
||||||
|
ingameClient.registerHandler(KeyF12, down, proc() = toggleSpec())
|
||||||
|
ingameClient.registerHandler(KeyF11, down, toggleShipSelect)
|
||||||
|
ingameClient.registerHandler(MouseLeft, down, handleLClick)
|
||||||
|
when defined(recordMode):
|
||||||
|
if not existsDir("data/snapshots"):
|
||||||
|
createDir("data/snapshots")
|
||||||
|
ingameClient.registerHandler(keynum9, down, proc() =
|
||||||
|
if not isRecording: startRecording()
|
||||||
|
else: stopRecording())
|
||||||
|
ingameClient.registerHandler(keynum0, down, proc() =
|
||||||
|
if snapshots.len > 0 and not isRecording:
|
||||||
|
echo "Saving images (LOL)"
|
||||||
|
for i in 0..high(snapshots):
|
||||||
|
if not(snapshots[i].save("data/snapshots/image"&(zeroPad($i, 3))&".jpg")):
|
||||||
|
echo "Could not save"
|
||||||
|
snapshots[i].destroy()
|
||||||
|
snapshots.setLen 0)
|
||||||
|
when defined(DebugKeys):
|
||||||
|
ingameClient.registerHandler MouseRight, down, proc() =
|
||||||
|
echo($activevehicle.body.getAngle.vectorForAngle())
|
||||||
|
ingameClient.registerHandler KeyBackslash, down, proc() =
|
||||||
|
createBot()
|
||||||
|
ingameClient.registerHandler(KeyNum1, down, proc() =
|
||||||
|
if localPlayer.items.len == 0:
|
||||||
|
localPlayer.addItem("Mass Driver")
|
||||||
|
echo "Gave you a mass driverz")
|
||||||
|
ingameClient.registerHandler(KeyL, down, proc() =
|
||||||
|
echo("len(livebullets) = ", len(livebullets)))
|
||||||
|
ingameClient.registerHandler(KeyRShift, down, proc() =
|
||||||
|
if keyPressed(KeyR):
|
||||||
|
echo("Friction: ", ff(activeVehicle.shape.getFriction()))
|
||||||
|
echo("Damping: ", ff(space.getDamping()))
|
||||||
|
elif keypressed(KeyM):
|
||||||
|
echo("Mass: ", activeVehicle.body.getMass.ff())
|
||||||
|
echo("Moment: ", activeVehicle.body.getMoment.ff())
|
||||||
|
elif keypressed(KeyI):
|
||||||
|
echo(repr(activeVehicle.record))
|
||||||
|
elif keyPressed(KeyH):
|
||||||
|
activeVehicle.body.setPos(vector(100.0, 100.0))
|
||||||
|
activeVehicle.body.setVel(vectorZero)
|
||||||
|
elif keyPressed(KeyComma):
|
||||||
|
activeVehicle.body.setPos mouseToSpace())
|
||||||
|
ingameClient.registerHandler(KeyY, down, proc() =
|
||||||
|
const looloo = ["Asteroid1", "Asteroid2"]
|
||||||
|
addObject(looloo[random(looloo.len)]))
|
||||||
|
ingameClient.registerHandler(KeyO, down, proc() =
|
||||||
|
if objects.len == 0:
|
||||||
|
echo "Objects is empty"
|
||||||
|
return
|
||||||
|
for i, o in pairs(objects):
|
||||||
|
echo i, " ", o)
|
||||||
|
ingameClient.registerHandler(KeyLBracket, down, sound_buffer.report)
|
||||||
|
var
|
||||||
|
mouseJoint: PConstraint
|
||||||
|
mouseBody = space.addBody(newBody(CpInfinity, CpInfinity))
|
||||||
|
ingameClient.registerHandler(MouseMiddle, down, proc() =
|
||||||
|
var point = mouseToSpace()
|
||||||
|
var shape = space.pointQueryFirst(point, LGrabbable, 0)
|
||||||
|
if not mouseJoint.isNil:
|
||||||
|
space.removeConstraint mouseJoint
|
||||||
|
mouseJoint.destroy()
|
||||||
|
mouseJoint = nil
|
||||||
|
if shape.isNil:
|
||||||
|
return
|
||||||
|
let body = shape.getBody()
|
||||||
|
mouseJoint = space.addConstraint(
|
||||||
|
newPivotJoint(mouseBody, body, vectorZero, body.world2local(point)))
|
||||||
|
mouseJoint.maxForce = 50000.0
|
||||||
|
mouseJoint.errorBias = pow(1.0 - 0.15, 60))
|
||||||
|
|
||||||
|
var specCameraSpeed = 5.0
|
||||||
|
specInputClient.registerHandler(MouseLeft, down, handleLClick)
|
||||||
|
specInputClient.registerHandler(KeyF11, down, toggleShipSelect)
|
||||||
|
specInputClient.registerHandler(KeyF12, down, proc() = toggleSpec())
|
||||||
|
specInputClient.registerHandler(KeyLShift, down, proc() = specCameraSpeed *= 2)
|
||||||
|
specInputClient.registerHandler(KeyLShift, up, proc() = specCameraSpeed /= 2)
|
||||||
|
|
||||||
|
specInputClient.registerHandler(KeyP, down, proc() =
|
||||||
|
echo("addObject(solar mold)")
|
||||||
|
addObject("Solar Mold"))
|
||||||
|
|
||||||
|
proc resetForcesCB(body: PBody; data: pointer) {.cdecl.} =
|
||||||
|
body.resetForces()
|
||||||
|
|
||||||
|
var frameCount= 0
|
||||||
|
proc mainUpdate(dt: float) =
|
||||||
|
if localPlayer.spectator:
|
||||||
|
if keyPressed(KeyLeft):
|
||||||
|
worldView.move(vec2f(-1.0, 0.0) * specCameraSpeed)
|
||||||
|
elif keyPressed(KeyRight):
|
||||||
|
worldView.move(vec2f( 1.0, 0.0) * specCameraSpeed)
|
||||||
|
if keyPressed(KeyUp):
|
||||||
|
worldView.move(vec2f(0.0, -1.0) * specCameraSpeed)
|
||||||
|
elif keyPressed(KeyDown):
|
||||||
|
worldView.move(vec2f(0.0, 1.0) * specCameraSpeed)
|
||||||
|
elif not activeVehicle.isNil:
|
||||||
|
if keyPressed(KeyUp):
|
||||||
|
activeVehicle.accel(dt)
|
||||||
|
elif keyPressed(keyDown):
|
||||||
|
activeVehicle.reverse(dt)
|
||||||
|
if keyPressed(KeyRight):
|
||||||
|
activeVehicle.turn_right(dt)
|
||||||
|
elif keyPressed(KeyLeft):
|
||||||
|
activeVehicle.turn_left(dt)
|
||||||
|
if keyPressed(keyz):
|
||||||
|
activeVehicle.strafe_left(dt)
|
||||||
|
elif keyPressed(keyx):
|
||||||
|
activeVehicle.strafe_right(dt)
|
||||||
|
if keyPressed(KeyLControl):
|
||||||
|
localPlayer.useItem 0
|
||||||
|
if keyPressed(KeyTab):
|
||||||
|
localPlayer.useItem 1
|
||||||
|
if keyPressed(KeyQ):
|
||||||
|
localPlayer.useItem 2
|
||||||
|
if keyPressed(KeyW):
|
||||||
|
localPlayer.useItem 3
|
||||||
|
if Keypressed(keyA):
|
||||||
|
localPlayer.useItem 4
|
||||||
|
if keyPressed(sfml.KeyS):
|
||||||
|
localPlayer.useItem 5
|
||||||
|
if keyPressed(KeyD):
|
||||||
|
localPlayer.useItem 6
|
||||||
|
worldView.setCenter(activeVehicle.body.getPos.floor)#cp2sfml)
|
||||||
|
|
||||||
|
if localPlayer != nil:
|
||||||
|
localPlayer.update()
|
||||||
|
localPlayer.updateItems(dt)
|
||||||
|
for b in localBots:
|
||||||
|
b.update()
|
||||||
|
|
||||||
|
for o in items(objects):
|
||||||
|
o.update(dt)
|
||||||
|
for i in countdown(high(delObjects), 0):
|
||||||
|
objects.del i
|
||||||
|
delObjects.setLen 0
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while i < len(liveBullets):
|
||||||
|
if liveBullets[i].update(dt):
|
||||||
|
liveBullets.del i
|
||||||
|
else:
|
||||||
|
inc i
|
||||||
|
i = 0
|
||||||
|
while i < len(explosions):
|
||||||
|
if explosions[i].next(dt): inc i
|
||||||
|
else: explosions.del i
|
||||||
|
|
||||||
|
when defined(DebugKeys):
|
||||||
|
mouseBody.setPos(mouseToSpace())
|
||||||
|
|
||||||
|
space.step(dt)
|
||||||
|
space.eachBody(resetForcesCB, nil)
|
||||||
|
|
||||||
|
when defined(foo):
|
||||||
|
var coords = window.convertCoords(vec2i(getMousePos()), worldView)
|
||||||
|
mouseSprite.setPosition(coords)
|
||||||
|
|
||||||
|
if localPlayer != nil and localPlayer.vehicle != nil:
|
||||||
|
let
|
||||||
|
pos = localPlayer.vehicle.body.getPos()
|
||||||
|
ang = localPlayer.vehicle.body.getAngle.vectorForAngle()
|
||||||
|
myPosition[0].x = pos.x
|
||||||
|
myPosition[0].z = pos.y
|
||||||
|
myPosition[1].x = ang.x
|
||||||
|
myPosition[1].z = ang.y
|
||||||
|
listenerSetPosition(myPosition[0])
|
||||||
|
listenerSetDirection(myPosition[1])
|
||||||
|
|
||||||
|
inc frameCount
|
||||||
|
when defined(showFPS):
|
||||||
|
if frameCount mod 60 == 0:
|
||||||
|
fpsText.setString($(1.0/dt).round)
|
||||||
|
if frameCount mod 250 == 0:
|
||||||
|
updateSoundBuffer()
|
||||||
|
frameCount = 0
|
||||||
|
|
||||||
|
proc mainRender() =
|
||||||
|
window.clear(Black)
|
||||||
|
window.setView(worldView)
|
||||||
|
|
||||||
|
if showStars:
|
||||||
|
for star in stars:
|
||||||
|
window.draw(star.sprite)
|
||||||
|
window.draw(localPlayer)
|
||||||
|
|
||||||
|
for b in localBots:
|
||||||
|
window.draw(b)
|
||||||
|
for o in objects:
|
||||||
|
window.draw(o)
|
||||||
|
|
||||||
|
for b in explosions: window.draw(b)
|
||||||
|
for b in liveBullets: window.draw(b)
|
||||||
|
|
||||||
|
when defined(Foo):
|
||||||
|
window.draw(mouseSprite)
|
||||||
|
|
||||||
|
window.setView(guiView)
|
||||||
|
|
||||||
|
when defined(EscapeMenuTest):
|
||||||
|
if escMenuOpen:
|
||||||
|
window.draw escMenu
|
||||||
|
when defined(showFPS):
|
||||||
|
window.draw(fpsText)
|
||||||
|
when defined(recordMode):
|
||||||
|
window.draw(recordButton)
|
||||||
|
|
||||||
|
if localPlayer.spectator:
|
||||||
|
window.draw(specGui)
|
||||||
|
if showShipSelect: window.draw shipSelect
|
||||||
|
window.display()
|
||||||
|
|
||||||
|
when defined(recordMode):
|
||||||
|
if isRecording:
|
||||||
|
if snapshots.len < 100:
|
||||||
|
if frameCount mod 5 == 0:
|
||||||
|
snapshots.add(window.capture())
|
||||||
|
else: stopRecording()
|
||||||
|
|
||||||
|
proc readyMainState() =
|
||||||
|
specInputClient.setActive()
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
import parseopt
|
||||||
|
|
||||||
|
localPlayer = newPlayer()
|
||||||
|
LobbyInit()
|
||||||
|
|
||||||
|
videoMode = getClientSettings().resolution
|
||||||
|
window = newRenderWindow(videoMode, "sup", sfDefaultStyle)
|
||||||
|
window.setFrameRateLimit 60
|
||||||
|
|
||||||
|
worldView = window.getView.copy()
|
||||||
|
guiView = worldView.copy()
|
||||||
|
shipSelect.setPosition vec2f(665.0, 50.0)
|
||||||
|
|
||||||
|
when defined(foo):
|
||||||
|
mouseSprite = sfml.newCircleShape(14)
|
||||||
|
mouseSprite.setFillColor Transparent
|
||||||
|
mouseSprite.setOutlineColor RoyalBlue
|
||||||
|
mouseSprite.setOutlineThickness 1.4
|
||||||
|
mouseSprite.setOrigin vec2f(14, 14)
|
||||||
|
|
||||||
|
LobbyReady()
|
||||||
|
playBtn = specGui.newButton(
|
||||||
|
"Unspec - F12", position = vec2f(680.0, 8.0), onClick = proc(b: PButton) =
|
||||||
|
toggleSpec())
|
||||||
|
|
||||||
|
block:
|
||||||
|
var bPlayOffline = false
|
||||||
|
for kind, key, val in getOpt():
|
||||||
|
case kind
|
||||||
|
of cmdArgument:
|
||||||
|
if key == "offline": bPlayOffline = true
|
||||||
|
else:
|
||||||
|
echo "Invalid argument ", key, " ", val
|
||||||
|
if bPlayOffline:
|
||||||
|
playoffline(nil)
|
||||||
|
|
||||||
|
gameRunning = true
|
||||||
|
while gameRunning:
|
||||||
|
for event in window.filterEvents:
|
||||||
|
if event.kind == EvtClosed:
|
||||||
|
gameRunning = false
|
||||||
|
break
|
||||||
|
elif event.kind == EvtMouseWheelMoved and getActiveState() == Field:
|
||||||
|
if event.mouseWheel.delta == 1:
|
||||||
|
worldView.zoom(0.9)
|
||||||
|
else:
|
||||||
|
worldView.zoom(1.1)
|
||||||
|
let dt = frameRate.restart.asMilliSeconds().float / 1000.0
|
||||||
|
case getActiveState()
|
||||||
|
of Field:
|
||||||
|
mainUpdate(dt)
|
||||||
|
mainRender()
|
||||||
|
of Lobby:
|
||||||
|
lobbyUpdate(dt)
|
||||||
|
lobbyDraw(window)
|
||||||
|
else:
|
||||||
|
initLevel()
|
||||||
|
echo("Done? lol")
|
||||||
|
doneWithSaidTransition()
|
||||||
|
readyMainState()
|
||||||
8
tests/manyloc/keineschweine/keineschweine.nimrod.cfg
Normal file
8
tests/manyloc/keineschweine/keineschweine.nimrod.cfg
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
path = "lib"
|
||||||
|
path = "dependencies/sfml"
|
||||||
|
path = "dependencies/chipmunk"
|
||||||
|
path = "dependencies/nake"
|
||||||
|
path = "dependencies/enet"
|
||||||
|
path = "dependencies/genpacket"
|
||||||
|
path = "enet_server"
|
||||||
|
debugger = off
|
||||||
75
tests/manyloc/keineschweine/lib/animations.nim
Normal file
75
tests/manyloc/keineschweine/lib/animations.nim
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import
|
||||||
|
math,
|
||||||
|
sfml, chipmunk,
|
||||||
|
sg_assets, sfml_stuff, math_helpers
|
||||||
|
type
|
||||||
|
PAnimation* = ref TAnimation
|
||||||
|
TAnimation* = object
|
||||||
|
sprite*: PSprite
|
||||||
|
record*: PAnimationRecord
|
||||||
|
delay*: float
|
||||||
|
index*: int
|
||||||
|
direction*: int
|
||||||
|
spriteRect*: TIntRect
|
||||||
|
style*: TAnimationStyle
|
||||||
|
TAnimationStyle* = enum
|
||||||
|
AnimLoop = 0'i8, AnimBounce, AnimOnce
|
||||||
|
|
||||||
|
proc setPos*(obj: PAnimation; pos: TVector) {.inline.}
|
||||||
|
proc setPos*(obj: PAnimation; pos: TVector2f) {.inline.}
|
||||||
|
proc setAngle*(obj: PAnimation; radians: float) {.inline.}
|
||||||
|
|
||||||
|
proc free*(obj: PAnimation) =
|
||||||
|
obj.sprite.destroy()
|
||||||
|
obj.record = nil
|
||||||
|
|
||||||
|
proc newAnimation*(src: PAnimationRecord; style: TAnimationStyle): PAnimation =
|
||||||
|
new(result, free)
|
||||||
|
result.sprite = src.spriteSheet.sprite.copy()
|
||||||
|
result.record = src
|
||||||
|
result.delay = src.delay
|
||||||
|
result.index = 0
|
||||||
|
result.direction = 1
|
||||||
|
result.spriteRect = result.sprite.getTextureRect()
|
||||||
|
result.style = style
|
||||||
|
proc newAnimation*(src: PAnimationRecord; style: TAnimationStyle;
|
||||||
|
pos: TVector2f; angle: float): PAnimation =
|
||||||
|
result = newAnimation(src, style)
|
||||||
|
result.setPos pos
|
||||||
|
setAngle(result, angle)
|
||||||
|
|
||||||
|
proc next*(obj: PAnimation; dt: float): bool {.discardable.} =
|
||||||
|
## step the animation. Returns false if the object is out of frames
|
||||||
|
result = true
|
||||||
|
obj.delay -= dt
|
||||||
|
if obj.delay <= 0.0:
|
||||||
|
obj.delay += obj.record.delay
|
||||||
|
obj.index += obj.direction
|
||||||
|
#if obj.index > (obj.record.spriteSheet.cols - 1) or obj.index < 0:
|
||||||
|
if not(obj.index in 0..(obj.record.spriteSheet.cols - 1)):
|
||||||
|
case obj.style
|
||||||
|
of AnimOnce:
|
||||||
|
return false
|
||||||
|
of AnimBounce:
|
||||||
|
obj.direction *= -1
|
||||||
|
obj.index += obj.direction * 2
|
||||||
|
of AnimLoop:
|
||||||
|
obj.index = 0
|
||||||
|
obj.spriteRect.left = obj.index.cint * obj.record.spriteSheet.frameW.cint
|
||||||
|
obj.sprite.setTextureRect obj.spriteRect
|
||||||
|
|
||||||
|
proc setPos*(obj: PAnimation; pos: TVector) =
|
||||||
|
setPosition(obj.sprite, pos.floor())
|
||||||
|
proc setPos*(obj: PAnimation; pos: TVector2f) =
|
||||||
|
setPosition(obj.sprite, pos)
|
||||||
|
proc setAngle*(obj: PAnimation; radians: float) =
|
||||||
|
let rads = (radians + obj.record.angle).wmod(TAU)
|
||||||
|
if obj.record.spriteSheet.rows > 1:
|
||||||
|
## (rotation percent * rows).floor * frameheight
|
||||||
|
obj.spriteRect.top = (rads / TAU * obj.record.spriteSheet.rows.float).floor.cint * obj.record.spriteSheet.frameh.cint
|
||||||
|
obj.sprite.setTextureRect obj.spriteRect
|
||||||
|
else:
|
||||||
|
setRotation(obj.sprite, degrees(rads)) #stupid sfml, who uses degrees these days? -__-
|
||||||
|
|
||||||
|
proc draw*(window: PRenderWindow; obj: PAnimation) {.inline.} =
|
||||||
|
window.draw(obj.sprite)
|
||||||
142
tests/manyloc/keineschweine/lib/client_helpers.nim
Normal file
142
tests/manyloc/keineschweine/lib/client_helpers.nim
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
import
|
||||||
|
tables, sg_packets, enet, estreams, sg_gui, sfml,
|
||||||
|
zlib_helpers, md5, sg_assets, os
|
||||||
|
type
|
||||||
|
PServer* = ptr TServer
|
||||||
|
TServer* = object
|
||||||
|
connected*: bool
|
||||||
|
addy: enet.TAddress
|
||||||
|
host*: PHost
|
||||||
|
peer*: PPeer
|
||||||
|
handlers*: TTable[char, TScPktHandler]
|
||||||
|
TScPktHandler* = proc(serv: PServer; buffer: PBuffer)
|
||||||
|
TFileTransfer = object
|
||||||
|
fileName: string
|
||||||
|
assetType: TAssetType
|
||||||
|
fullLen: int
|
||||||
|
pos: int32
|
||||||
|
data: string
|
||||||
|
readyToSave: bool
|
||||||
|
var
|
||||||
|
currentFileTransfer: TFileTransfer
|
||||||
|
downloadProgress* = newButton(nil, "", vec2f(0,0), nil)
|
||||||
|
currentFileTransfer.data = ""
|
||||||
|
|
||||||
|
proc addHandler*(serv: PServer; packetType: char; handler: TScPktHandler) =
|
||||||
|
serv.handlers[packetType] = handler
|
||||||
|
|
||||||
|
proc newServer*(): PServer =
|
||||||
|
result = cast[PServer](alloc0(sizeof(TServer)))
|
||||||
|
result.connected = false
|
||||||
|
result.host = createHost(nil, 1, 2, 0, 0)
|
||||||
|
result.handlers = initTable[char, TScPktHandler](32)
|
||||||
|
|
||||||
|
proc connect*(serv: PServer; host: string; port: int16; error: var string): bool =
|
||||||
|
if setHost(serv.addy, host) != 0:
|
||||||
|
error = "Could not resolve host "
|
||||||
|
error.add host
|
||||||
|
return false
|
||||||
|
serv.addy.port = port.cushort
|
||||||
|
serv.peer = serv.host.connect(serv.addy, 2, 0)
|
||||||
|
if serv.peer.isNil:
|
||||||
|
error = "Could not connect to host "
|
||||||
|
error.add host
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc send*[T](serv: PServer; packetType: char; pkt: var T) =
|
||||||
|
if serv.connected:
|
||||||
|
var b = newBuffer(100)
|
||||||
|
b.write packetType
|
||||||
|
b.pack pkt
|
||||||
|
serv.peer.send(0.cuchar, b, FlagUnsequenced)
|
||||||
|
|
||||||
|
proc sendPubChat*(server: PServer; msg: string) =
|
||||||
|
var chat = newCsChat("", msg)
|
||||||
|
server.send HChat, chat
|
||||||
|
|
||||||
|
proc handlePackets*(server: PServer; buf: PBuffer) =
|
||||||
|
while not buf.atEnd():
|
||||||
|
let typ = readChar(buf)
|
||||||
|
if server.handlers.hasKey(typ):
|
||||||
|
server.handlers[typ](server, buf)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
proc updateFileProgress*() =
|
||||||
|
let progress = currentFileTransfer.pos / currentFileTransfer.fullLen
|
||||||
|
downloadProgress.bg.setSize(vec2f(progress * 100, 20))
|
||||||
|
downloadProgress.setString($currentFileTransfer.pos &'/'& $currentFileTransfer.fullLen)
|
||||||
|
|
||||||
|
## HFileTransfer
|
||||||
|
proc handleFilePartRecv*(serv: PServer; buffer: PBuffer) {.procvar.} =
|
||||||
|
var
|
||||||
|
f = readScFileTransfer(buffer)
|
||||||
|
updateFileProgress()
|
||||||
|
if not(f.pos == currentFileTransfer.pos):
|
||||||
|
echo "returning early from filepartrecv"
|
||||||
|
return ##issues, probably
|
||||||
|
if currentFileTransfer.data.len == 0:
|
||||||
|
echo "setting current file size"
|
||||||
|
currentFileTransfer.data.setLen f.fileSize
|
||||||
|
let len = f.data.len
|
||||||
|
copymem(
|
||||||
|
addr currentFileTransfer.data[f.pos],
|
||||||
|
addr f.data[0],
|
||||||
|
len)
|
||||||
|
currentFileTransfer.pos = f.pos + len.int32
|
||||||
|
if currentFileTransfer.pos == f.fileSize: #file should be done, rizzight
|
||||||
|
currentFileTransfer.data = uncompress(
|
||||||
|
currentFileTransfer.data, currentFileTransfer.fullLen)
|
||||||
|
currentFileTransfer.readyToSave = true
|
||||||
|
var resp: CsFileChallenge
|
||||||
|
resp.checksum = toMD5(currentFileTransfer.data)
|
||||||
|
serv.send HFileChallenge, resp
|
||||||
|
echo "responded with challenge (ready to save)"
|
||||||
|
else:
|
||||||
|
var resp = newCsFilepartAck(currentFileTransfer.pos)
|
||||||
|
serv.send HFileTransfer, resp
|
||||||
|
echo "responded for next part"
|
||||||
|
|
||||||
|
proc saveCurrentFile() =
|
||||||
|
if not currentFileTransfer.readyToSave: return
|
||||||
|
let
|
||||||
|
path = expandPath(currentFileTransfer.assetType, currentFileTransfer.fileName)
|
||||||
|
parent = parentDir(path)
|
||||||
|
if not existsDir(parent):
|
||||||
|
createDir(parent)
|
||||||
|
echo("Created dir")
|
||||||
|
writeFile path, currentFIleTransfer.data
|
||||||
|
echo "Write file"
|
||||||
|
|
||||||
|
## HChallengeResult
|
||||||
|
proc handleFileChallengeResult*(serv: PServer; buffer: PBuffer) {.procvar.} =
|
||||||
|
var res = readScChallengeResult(buffer).status
|
||||||
|
echo "got challnege result: ", res
|
||||||
|
if res and currentFileTransfer.readyToSave:
|
||||||
|
echo "saving"
|
||||||
|
saveCurrentFile()
|
||||||
|
else:
|
||||||
|
currentFileTransfer.readyToSave = false
|
||||||
|
currentFileTransfer.pos = 0
|
||||||
|
echo "REsetting current file"
|
||||||
|
|
||||||
|
## HFileCHallenge
|
||||||
|
proc handleFileChallenge*(serv: PServer; buffer: PBuffer) {.procvar.} =
|
||||||
|
var
|
||||||
|
challenge = readScFileChallenge(buffer)
|
||||||
|
path = expandPath(challenge)
|
||||||
|
resp: CsFileChallenge
|
||||||
|
if not existsFile(path):
|
||||||
|
resp.needFile = true
|
||||||
|
echo "Got file challenge, need file."
|
||||||
|
else:
|
||||||
|
resp.checksum = toMD5(readFile(path))
|
||||||
|
echo "got file challenge, sending sum"
|
||||||
|
currentFileTransfer.fileName = challenge.file
|
||||||
|
currentFileTransfer.assetType = challenge.assetType
|
||||||
|
currentFileTransfer.fullLen = challenge.fullLen.int
|
||||||
|
currentFileTransfer.pos = 0
|
||||||
|
currentFileTransfer.data.setLen 0
|
||||||
|
currentFileTransfer.readyToSave = false
|
||||||
|
serv.send HFileChallenge, resp
|
||||||
126
tests/manyloc/keineschweine/lib/estreams.nim
Normal file
126
tests/manyloc/keineschweine/lib/estreams.nim
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
import endians
|
||||||
|
|
||||||
|
proc swapEndian16*(outp, inp: pointer) =
|
||||||
|
## copies `inp` to `outp` swapping bytes. Both buffers are supposed to
|
||||||
|
## contain at least 2 bytes.
|
||||||
|
var i = cast[cstring](inp)
|
||||||
|
var o = cast[cstring](outp)
|
||||||
|
o[0] = i[1]
|
||||||
|
o[1] = i[0]
|
||||||
|
when cpuEndian == bigEndian:
|
||||||
|
proc bigEndian16(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 2)
|
||||||
|
else:
|
||||||
|
proc bigEndian16*(outp, inp: pointer) {.inline.} = swapEndian16(outp, inp)
|
||||||
|
|
||||||
|
import enet
|
||||||
|
|
||||||
|
type
|
||||||
|
PBuffer* = ref object
|
||||||
|
data*: string
|
||||||
|
pos: int
|
||||||
|
|
||||||
|
proc free(b: PBuffer) =
|
||||||
|
GCunref b.data
|
||||||
|
proc newBuffer*(len: int): PBuffer =
|
||||||
|
new result, free
|
||||||
|
result.data = newString(len)
|
||||||
|
proc newBuffer*(pkt: PPacket): PBuffer =
|
||||||
|
new result, free
|
||||||
|
result.data = newString(pkt.dataLength)
|
||||||
|
copyMem(addr result.data[0], pkt.data, pkt.dataLength)
|
||||||
|
proc toPacket*(buffer: PBuffer; flags: TPacketFlag): PPacket =
|
||||||
|
buffer.data.setLen buffer.pos
|
||||||
|
result = createPacket(cstring(buffer.data), buffer.pos, flags)
|
||||||
|
|
||||||
|
proc isDirty*(buffer: PBuffer): bool {.inline.} =
|
||||||
|
result = (buffer.pos != 0)
|
||||||
|
proc atEnd*(buffer: PBuffer): bool {.inline.} =
|
||||||
|
result = (buffer.pos == buffer.data.len)
|
||||||
|
proc reset*(buffer: PBuffer) {.inline.} =
|
||||||
|
buffer.pos = 0
|
||||||
|
|
||||||
|
proc flush*(buf: PBuffer) =
|
||||||
|
buf.pos = 0
|
||||||
|
buf.data.setLen(0)
|
||||||
|
proc send*(peer: PPeer; channel: cuchar; buf: PBuffer; flags: TPacketFlag): cint {.discardable.} =
|
||||||
|
result = send(peer, channel, buf.toPacket(flags))
|
||||||
|
|
||||||
|
proc read*[T: int16|uint16](buffer: PBuffer; outp: var T) =
|
||||||
|
bigEndian16(addr outp, addr buffer.data[buffer.pos])
|
||||||
|
inc buffer.pos, 2
|
||||||
|
proc read*[T: float32|int32|uint32](buffer: PBuffer; outp: var T) =
|
||||||
|
bigEndian32(addr outp, addr buffer.data[buffer.pos])
|
||||||
|
inc buffer.pos, 4
|
||||||
|
proc read*[T: float64|int64|uint64](buffer: PBuffer; outp: var T) =
|
||||||
|
bigEndian64(addr outp, addr buffer.data[buffer.pos])
|
||||||
|
inc buffer.pos, 8
|
||||||
|
proc read*[T: int8|uint8|byte|bool|char](buffer: PBuffer; outp: var T) =
|
||||||
|
copyMem(addr outp, addr buffer.data[buffer.pos], 1)
|
||||||
|
inc buffer.pos, 1
|
||||||
|
|
||||||
|
proc writeBE*[T: int16|uint16](buffer: PBuffer; val: var T) =
|
||||||
|
setLen buffer.data, buffer.pos + 2
|
||||||
|
bigEndian16(addr buffer.data[buffer.pos], addr val)
|
||||||
|
inc buffer.pos, 2
|
||||||
|
proc writeBE*[T: int32|uint32|float32](buffer: PBuffer; val: var T) =
|
||||||
|
setLen buffer.data, buffer.pos + 4
|
||||||
|
bigEndian32(addr buffer.data[buffer.pos], addr val)
|
||||||
|
inc buffer.pos, 4
|
||||||
|
proc writeBE*[T: int64|uint64|float64](buffer: PBuffer; val: var T) =
|
||||||
|
setLen buffer.data, buffer.pos + 8
|
||||||
|
bigEndian64(addr buffer.data[buffer.pos], addr val)
|
||||||
|
inc buffer.pos, 8
|
||||||
|
proc writeBE*[T: char|int8|uint8|byte|bool](buffer: PBuffer; val: var T) =
|
||||||
|
setLen buffer.data, buffer.pos + 1
|
||||||
|
copyMem(addr buffer.data[buffer.pos], addr val, 1)
|
||||||
|
inc buffer.pos, 1
|
||||||
|
|
||||||
|
|
||||||
|
proc write*(buffer: PBuffer; val: var string) =
|
||||||
|
var length = len(val).uint16
|
||||||
|
writeBE buffer, length
|
||||||
|
setLen buffer.data, buffer.pos + length.int
|
||||||
|
copyMem(addr buffer.data[buffer.pos], addr val[0], length.int)
|
||||||
|
inc buffer.pos, length.int
|
||||||
|
proc write*[T: TNumber|bool|char|byte](buffer: PBuffer; val: T) =
|
||||||
|
var v: T
|
||||||
|
shallowCopy v, val
|
||||||
|
writeBE buffer, v
|
||||||
|
|
||||||
|
proc readInt8*(buffer: PBuffer): int8 =
|
||||||
|
read buffer, result
|
||||||
|
proc readInt16*(buffer: PBuffer): int16 =
|
||||||
|
read buffer, result
|
||||||
|
proc readInt32*(buffer: PBuffer): int32 =
|
||||||
|
read buffer, result
|
||||||
|
proc readInt64*(buffer: PBuffer): int64 =
|
||||||
|
read buffer, result
|
||||||
|
proc readFloat32*(buffer: PBuffer): float32 =
|
||||||
|
read buffer, result
|
||||||
|
proc readFloat64*(buffer: PBuffer): float64 =
|
||||||
|
read buffer, result
|
||||||
|
proc readStr*(buffer: PBuffer): string =
|
||||||
|
let len = readInt16(buffer).int
|
||||||
|
result = ""
|
||||||
|
if len > 0:
|
||||||
|
result.setLen len
|
||||||
|
copyMem(addr result[0], addr buffer.data[buffer.pos], len)
|
||||||
|
inc buffer.pos, len
|
||||||
|
proc readChar*(buffer: PBuffer): char {.inline.} = return readInt8(buffer).char
|
||||||
|
proc readBool*(buffer: PBuffer): bool {.inline.} = return readInt8(buffer).bool
|
||||||
|
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
var b = newBuffer(100)
|
||||||
|
var str = "hello there"
|
||||||
|
b.write str
|
||||||
|
echo(repr(b))
|
||||||
|
b.pos = 0
|
||||||
|
echo(repr(b.readStr()))
|
||||||
|
|
||||||
|
b.flush()
|
||||||
|
echo "flushed"
|
||||||
|
b.writeC([1,2,3])
|
||||||
|
echo(repr(b))
|
||||||
|
|
||||||
|
|
||||||
34
tests/manyloc/keineschweine/lib/game_objects.nim
Normal file
34
tests/manyloc/keineschweine/lib/game_objects.nim
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import chipmunk, sfml, animations, sg_assets
|
||||||
|
type
|
||||||
|
PGameObject* = ref TGameObject
|
||||||
|
TGameObject = object
|
||||||
|
body*: chipmunk.PBody
|
||||||
|
shape*: chipmunk.PShape
|
||||||
|
record*: PObjectRecord
|
||||||
|
anim*: PAnimation
|
||||||
|
|
||||||
|
|
||||||
|
proc `$`*(obj: PGameObject): string =
|
||||||
|
result = "<Object "
|
||||||
|
result.add obj.record.name
|
||||||
|
result.add ' '
|
||||||
|
result.add($obj.body.getpos())
|
||||||
|
result.add '>'
|
||||||
|
proc free(obj: PGameObject) =
|
||||||
|
obj.record = nil
|
||||||
|
free(obj.anim)
|
||||||
|
obj.anim = nil
|
||||||
|
proc newObject*(record: PObjectRecord): PGameObject =
|
||||||
|
if record.isNil: return nil
|
||||||
|
new(result, free)
|
||||||
|
result.record = record
|
||||||
|
result.anim = newAnimation(record.anim, AnimLoop)
|
||||||
|
when false:
|
||||||
|
result.sprite = record.anim.spriteSheet.sprite.copy()
|
||||||
|
result.body = newBody(result.record.physics.mass, 10.0)
|
||||||
|
result.shape = chipmunk.newCircleShape(result.body, result.record.physics.radius, vectorZero)
|
||||||
|
result.body.setPos(vector(100, 100))
|
||||||
|
proc newObject*(name: string): PGameObject =
|
||||||
|
result = newObject(fetchObj(name))
|
||||||
|
proc draw*(window: PRenderWindow, obj: PGameObject) {.inline.} =
|
||||||
|
window.draw(obj.anim.sprite)
|
||||||
23
tests/manyloc/keineschweine/lib/idgen.nim
Normal file
23
tests/manyloc/keineschweine/lib/idgen.nim
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
type
|
||||||
|
PIDGen*[T: Ordinal] = ref TIDGen[T]
|
||||||
|
TIDGen*[T: Ordinal] = object
|
||||||
|
max: T
|
||||||
|
freeIDs: seq[T]
|
||||||
|
EOutOfIDs* = object of EInvalidKey
|
||||||
|
|
||||||
|
#proc free[T](idg: PIDgen[T]) =
|
||||||
|
# result.freeIDs = nil
|
||||||
|
proc newIDGen*[T: Ordinal](): PIDGen[T] =
|
||||||
|
new(result)#, free)
|
||||||
|
result.max = 0.T
|
||||||
|
result.freeIDs = @[]
|
||||||
|
proc next*[T](idg: PIDGen[T]): T =
|
||||||
|
if idg.freeIDs.len > 0:
|
||||||
|
result = idg.freeIDs.pop
|
||||||
|
elif idg.max < high(T)-T(1):
|
||||||
|
inc idg.max
|
||||||
|
result = idg.max
|
||||||
|
else:
|
||||||
|
raise newException(EOutOfIDs, "ID generator hit max value")
|
||||||
|
proc del*[T](idg: PIDGen[T]; id: T) =
|
||||||
|
idg.freeIDs.add id
|
||||||
138
tests/manyloc/keineschweine/lib/input_helpers.nim
Normal file
138
tests/manyloc/keineschweine/lib/input_helpers.nim
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
import
|
||||||
|
sfml, tables, hashes
|
||||||
|
type
|
||||||
|
TKeyEventKind* = enum down, up
|
||||||
|
TInputFinishedProc* = proc()
|
||||||
|
TKeyCallback = proc()
|
||||||
|
PKeyClient* = ref object
|
||||||
|
onKeyDown: TTable[int32, TKeyCallback]
|
||||||
|
onKeyUp: TTable[int32, TKeyCallback]
|
||||||
|
name: string
|
||||||
|
PTextInput* = ref object
|
||||||
|
text*: string
|
||||||
|
cursor: int
|
||||||
|
onEnter: TInputFinishedProc
|
||||||
|
var
|
||||||
|
keyState: array[-MouseButtonCount.int32 .. KeyCount.int32, bool]
|
||||||
|
mousePos: TVector2f
|
||||||
|
activeClient: PKeyClient = nil
|
||||||
|
activeInput: PTextInput = nil
|
||||||
|
|
||||||
|
proc setActive*(client: PKeyClient) =
|
||||||
|
activeClient = client
|
||||||
|
echo("** set active client ", client.name)
|
||||||
|
proc newKeyClient*(name: string = "unnamed", setactive = false): PKeyClient =
|
||||||
|
new(result)
|
||||||
|
result.onKeyDown = initTable[int32, TKeyCallback](16)
|
||||||
|
result.onKeyUp = initTable[int32, TKeyCallback](16)
|
||||||
|
result.name = name
|
||||||
|
if setActive:
|
||||||
|
setActive(result)
|
||||||
|
|
||||||
|
proc keyPressed*(key: TKeyCode): bool {.inline.} =
|
||||||
|
return keyState[key.int32]
|
||||||
|
proc buttonPressed*(btn: TMouseButton): bool {.inline.} =
|
||||||
|
return keyState[-btn.int32]
|
||||||
|
|
||||||
|
proc clearKey*(key: TKeyCode) {.inline.} =
|
||||||
|
keyState[key.int32] = false
|
||||||
|
proc clearButton*(btn: TMouseButton) {.inline.} =
|
||||||
|
keyState[-btn.int32] = false
|
||||||
|
|
||||||
|
proc addKeyEvent*(key: TKeyCode, ev: TKeyEventKind) {.inline.} =
|
||||||
|
if activeClient.isNil: return
|
||||||
|
let k = key.int32
|
||||||
|
case ev
|
||||||
|
of down:
|
||||||
|
keyState[k] = true
|
||||||
|
if activeClient.onKeyDown.hasKey(k):
|
||||||
|
activeClient.onKeyDown[k]()
|
||||||
|
else:
|
||||||
|
keyState[k] = false
|
||||||
|
if activeClient.onKeyUp.hasKey(k):
|
||||||
|
activeClient.onKeyUp[k]()
|
||||||
|
proc addButtonEvent*(btn: TMouseButton, ev: TKeyEventKind) {.inline.} =
|
||||||
|
if activeClient.isNil: return
|
||||||
|
let b = -btn.int32
|
||||||
|
case ev
|
||||||
|
of down:
|
||||||
|
keyState[b] = true
|
||||||
|
if activeClient.onKeyDown.hasKey(b):
|
||||||
|
activeClient.onKeyDown[b]()
|
||||||
|
else:
|
||||||
|
keyState[b] = false
|
||||||
|
if activeClient.onKeyUp.hasKey(b):
|
||||||
|
activeClient.onKeyUp[b]()
|
||||||
|
proc registerHandler*(client: PKeyClient; key: TKeyCode;
|
||||||
|
ev: TKeyEventKind; fn: TKeyCallback) =
|
||||||
|
case ev
|
||||||
|
of down: client.onKeyDown[key.int32] = fn
|
||||||
|
of up: client.onKeyUp[key.int32] = fn
|
||||||
|
proc registerHandler*(client: PKeyClient; btn: TMouseButton;
|
||||||
|
ev: TKeyEventKind; fn: TKeyCallback) =
|
||||||
|
case ev
|
||||||
|
of down: client.onKeyDown[-btn.int32] = fn
|
||||||
|
of up: client.onKeyUp[-btn.int32] = fn
|
||||||
|
|
||||||
|
proc newTextInput*(text = "", pos = 0, onEnter: TInputFinishedProc = nil): PTextInput =
|
||||||
|
new(result)
|
||||||
|
result.text = text
|
||||||
|
result.cursor = pos
|
||||||
|
result.onEnter = onEnter
|
||||||
|
proc setActive*(i: PTextInput) =
|
||||||
|
activeInput = i
|
||||||
|
proc stopCapturingText*() =
|
||||||
|
activeInput = nil
|
||||||
|
proc clear*(i: PTextInput) =
|
||||||
|
i.text.setLen 0
|
||||||
|
i.cursor = 0
|
||||||
|
proc recordText*(i: PTextInput; c: cint) =
|
||||||
|
if c > 127 or i.isNil: return
|
||||||
|
if c in 32..126: ##printable
|
||||||
|
if i.cursor == i.text.len: i.text.add(c.int.chr)
|
||||||
|
else:
|
||||||
|
let rem = i.text.substr(i.cursor)
|
||||||
|
i.text.setLen(i.cursor)
|
||||||
|
i.text.add(chr(c.int))
|
||||||
|
i.text.add(rem)
|
||||||
|
inc(i.cursor)
|
||||||
|
elif c == 8: ## \b backspace
|
||||||
|
if i.text.len > 0 and i.cursor > 0:
|
||||||
|
dec(i.cursor)
|
||||||
|
let rem = i.text.substr(i.cursor + 1)
|
||||||
|
i.text.setLen(i.cursor)
|
||||||
|
i.text.add(rem)
|
||||||
|
elif c == 10 or c == 13:## \n, \r enter
|
||||||
|
if not i.onEnter.isNil: i.onEnter()
|
||||||
|
proc recordText*(i: PTextInput; e: TTextEvent) {.inline.} =
|
||||||
|
recordText(i, e.unicode)
|
||||||
|
|
||||||
|
proc setMousePos*(x, y: cint) {.inline.} =
|
||||||
|
mousePos.x = x.float
|
||||||
|
mousePos.y = y.float
|
||||||
|
proc getMousePos*(): TVector2f {.inline.} = result = mousePos
|
||||||
|
|
||||||
|
var event: TEvent
|
||||||
|
# Handle and filter input-related events
|
||||||
|
iterator filterEvents*(window: PRenderWindow): PEvent =
|
||||||
|
while window.pollEvent(addr event):
|
||||||
|
case event.kind
|
||||||
|
of EvtKeyPressed: addKeyEvent(event.key.code, down)
|
||||||
|
of EvtKeyReleased: addKeyEvent(event.key.code, up)
|
||||||
|
of EvtMouseButtonPressed: addButtonEvent(event.mouseButton.button, down)
|
||||||
|
of EvtMouseButtonReleased: addButtonEvent(event.mouseButton.button, up)
|
||||||
|
of EvtTextEntered: recordText(activeInput, event.text)
|
||||||
|
of EvtMouseMoved: setMousePos(event.mouseMove.x, event.mouseMove.y)
|
||||||
|
else: yield(addr event)
|
||||||
|
# Handle and return input-related events
|
||||||
|
iterator pollEvents*(window: PRenderWindow): PEvent =
|
||||||
|
while window.pollEvent(addr event):
|
||||||
|
case event.kind
|
||||||
|
of EvtKeyPressed: addKeyEvent(event.key.code, down)
|
||||||
|
of EvtKeyReleased: addKeyEvent(event.key.code, up)
|
||||||
|
of EvtMouseButtonPressed: addButtonEvent(event.mouseButton.button, down)
|
||||||
|
of EvtMouseButtonReleased: addButtonEvent(event.mouseButton.button, up)
|
||||||
|
of EvtTextEntered: recordText(activeInput, event.text)
|
||||||
|
of EvtMouseMoved: setMousePos(event.mouseMove.x, event.mouseMove.y)
|
||||||
|
else: nil
|
||||||
|
yield(addr event)
|
||||||
41
tests/manyloc/keineschweine/lib/map_filter.nim
Normal file
41
tests/manyloc/keineschweine/lib/map_filter.nim
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
template filterIt2*(seq, pred: expr, body: stmt): stmt {.immediate, dirty.} =
|
||||||
|
## sequtils defines a filterIt() that returns a new seq, but this one is called
|
||||||
|
## with a statement body to iterate directly over it
|
||||||
|
for it in items(seq):
|
||||||
|
if pred: body
|
||||||
|
|
||||||
|
proc map*[A, B](x: seq[A], func: proc(y: A): B {.closure.}): seq[B] =
|
||||||
|
result = @[]
|
||||||
|
for item in x.items:
|
||||||
|
result.add func(item)
|
||||||
|
|
||||||
|
proc mapInPlace*[A](x: var seq[A], func: proc(y: A): A {.closure.}) =
|
||||||
|
for i in 0..x.len-1:
|
||||||
|
x[i] = func(x[i])
|
||||||
|
|
||||||
|
template unless*(condition: expr; body: stmt): stmt {.dirty.} =
|
||||||
|
if not(condition):
|
||||||
|
body
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
proc dumpSeq[T](x: seq[T]) =
|
||||||
|
for index, item in x.pairs:
|
||||||
|
echo index, " ", item
|
||||||
|
echo "-------"
|
||||||
|
|
||||||
|
var t= @[1,2,3,4,5]
|
||||||
|
var res = t.map(proc(z: int): int = result = z * 10)
|
||||||
|
dumpSeq res
|
||||||
|
|
||||||
|
from strutils import toHex, repeatStr
|
||||||
|
var foocakes = t.map(proc(z: int): string =
|
||||||
|
result = toHex((z * 23).biggestInt, 4))
|
||||||
|
dumpSeq foocakes
|
||||||
|
|
||||||
|
t.mapInPlace(proc(z: int): int = result = z * 30)
|
||||||
|
dumpSeq t
|
||||||
|
|
||||||
|
var someSeq = @[9,8,7,6,5,4,3,2,1] ## numbers < 6 or even
|
||||||
|
filterIt2 someSeq, it < 6 or (it and 1) == 0:
|
||||||
|
echo(it)
|
||||||
|
echo "-----------"
|
||||||
10
tests/manyloc/keineschweine/lib/math_helpers.nim
Normal file
10
tests/manyloc/keineschweine/lib/math_helpers.nim
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import strutils, math
|
||||||
|
|
||||||
|
proc degrees*(rad: float): float =
|
||||||
|
return rad * 180.0 / PI
|
||||||
|
proc radians*(deg: float): float =
|
||||||
|
return deg * PI / 180.0
|
||||||
|
|
||||||
|
## V not math, sue me
|
||||||
|
proc ff*(f: float, precision = 2): string {.inline.} =
|
||||||
|
return formatFloat(f, ffDecimal, precision)
|
||||||
37
tests/manyloc/keineschweine/lib/sfml_stuff.nim
Normal file
37
tests/manyloc/keineschweine/lib/sfml_stuff.nim
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import
|
||||||
|
math, strutils,
|
||||||
|
sfml, input_helpers
|
||||||
|
when not defined(NoChipmunk):
|
||||||
|
import chipmunk
|
||||||
|
proc floor*(a: TVector): TVector2f {.inline.} =
|
||||||
|
result.x = a.x.floor
|
||||||
|
result.y = a.y.floor
|
||||||
|
proc sfml2cp*(a: TVector2f): TVector {.inline.} =
|
||||||
|
result.x = a.x
|
||||||
|
result.y = a.y
|
||||||
|
proc cp2sfml*(a: TVector): TVector2f {.inline.} =
|
||||||
|
result.x = a.x
|
||||||
|
result.y = a.y
|
||||||
|
|
||||||
|
proc vec2f*(a: TVector2i): TVector2f =
|
||||||
|
result.x = a.x.cfloat
|
||||||
|
result.y = a.y.cfloat
|
||||||
|
proc vec2i*(a: TVector2f): TVector2i =
|
||||||
|
result.x = a.x.cint
|
||||||
|
result.y = a.y.cint
|
||||||
|
proc vec3f*(x, y, z: float): TVector3f =
|
||||||
|
result.x = x.cfloat
|
||||||
|
result.y = y.cfloat
|
||||||
|
result.z = z.cfloat
|
||||||
|
|
||||||
|
proc `$`*(a: var TIntRect): string =
|
||||||
|
result = "[TIntRect $1,$2 $3x$4]".format($a.left, $a.top, $a.width, $a.height)
|
||||||
|
proc `$`*(a: TKeyEvent): string =
|
||||||
|
return "KeyEvent: code=$1 alt=$2 control=$3 shift=$4 system=$5".format(
|
||||||
|
$a.code, $a.alt, $a.control, $a.shift, $a.system)
|
||||||
|
|
||||||
|
proc `wmod`*(x, y: float): float = return x - y * (x/y).floor
|
||||||
|
proc move*(a: var TIntRect, left, top: cint): bool =
|
||||||
|
if a.left != left or a.top != top: result = true
|
||||||
|
a.left = left
|
||||||
|
a.top = top
|
||||||
609
tests/manyloc/keineschweine/lib/sg_assets.nim
Normal file
609
tests/manyloc/keineschweine/lib/sg_assets.nim
Normal file
|
|
@ -0,0 +1,609 @@
|
||||||
|
import
|
||||||
|
re, json, strutils, tables, math, os, math_helpers,
|
||||||
|
sg_packets, md5, zlib_helpers
|
||||||
|
|
||||||
|
when defined(NoSFML):
|
||||||
|
import server_utils
|
||||||
|
type TVector2i = object
|
||||||
|
x*, y*: int32
|
||||||
|
proc vec2i(x, y: int32): TVector2i =
|
||||||
|
result.x = x
|
||||||
|
result.y = y
|
||||||
|
else:
|
||||||
|
import sfml, sfml_audio, sfml_stuff
|
||||||
|
when not defined(NoChipmunk):
|
||||||
|
import chipmunk
|
||||||
|
|
||||||
|
type
|
||||||
|
TChecksumFile* = object
|
||||||
|
unpackedSize*: int
|
||||||
|
sum*: MD5Digest
|
||||||
|
compressed*: string
|
||||||
|
PZoneSettings* = ref TZoneSettings
|
||||||
|
TZoneSettings* = object
|
||||||
|
vehicles: seq[PVehicleRecord]
|
||||||
|
items: seq[PItemRecord]
|
||||||
|
objects: seq[PObjectRecord]
|
||||||
|
bullets: seq[PBulletRecord]
|
||||||
|
levelSettings: PLevelSettings
|
||||||
|
PLevelSettings* = ref TLevelSettings
|
||||||
|
TLevelSettings* = object
|
||||||
|
size*: TVector2i
|
||||||
|
starfield*: seq[PSpriteSheet]
|
||||||
|
PVehicleRecord* = ref TVehicleRecord
|
||||||
|
TVehicleRecord* = object
|
||||||
|
id*: int16
|
||||||
|
name*: string
|
||||||
|
playable*: bool
|
||||||
|
anim*: PAnimationRecord
|
||||||
|
physics*: TPhysicsRecord
|
||||||
|
handling*: THandlingRecord
|
||||||
|
TItemKind* = enum
|
||||||
|
Projectile, Utility, Ammo
|
||||||
|
PObjectRecord* = ref TObjectRecord
|
||||||
|
TObjectRecord* = object
|
||||||
|
id*: int16
|
||||||
|
name*: string
|
||||||
|
anim*: PAnimationRecord
|
||||||
|
physics*: TPhysicsRecord
|
||||||
|
PItemRecord* = ref TItemRecord
|
||||||
|
TItemRecord* = object
|
||||||
|
id*: int16
|
||||||
|
name*: string
|
||||||
|
anim*: PAnimationRecord
|
||||||
|
physics*: TPhysicsRecord ##apply when the item is dropped in the arena
|
||||||
|
cooldown*: float
|
||||||
|
energyCost*: float
|
||||||
|
useSound*: PSoundRecord
|
||||||
|
case kind*: TItemKind
|
||||||
|
of Projectile:
|
||||||
|
bullet*: PBulletRecord
|
||||||
|
else:
|
||||||
|
nil
|
||||||
|
PBulletRecord* = ref TBulletRecord
|
||||||
|
TBulletRecord* = object
|
||||||
|
id*: int16
|
||||||
|
name*: string
|
||||||
|
anim*: PAnimationRecord
|
||||||
|
physics*: TPhysicsRecord
|
||||||
|
lifetime*, inheritVelocity*, baseVelocity*: float
|
||||||
|
explosion*: TExplosionRecord
|
||||||
|
trail*: TTrailRecord
|
||||||
|
TTrailRecord* = object
|
||||||
|
anim*: PAnimationRecord
|
||||||
|
timer*: float ##how often it should be created
|
||||||
|
TPhysicsRecord* = object
|
||||||
|
mass*: float
|
||||||
|
radius*: float
|
||||||
|
moment*: float
|
||||||
|
THandlingRecord = object
|
||||||
|
thrust*, top_speed*: float
|
||||||
|
reverse*, strafe*, rotation*: float
|
||||||
|
TSoulRecord = object
|
||||||
|
energy*: int
|
||||||
|
health*: int
|
||||||
|
TExplosionRecord* = object
|
||||||
|
anim*: PAnimationRecord
|
||||||
|
sound*: PSoundRecord
|
||||||
|
PAnimationRecord* = ref TAnimationRecord
|
||||||
|
TAnimationRecord* = object
|
||||||
|
spriteSheet*: PSpriteSheet
|
||||||
|
angle*: float
|
||||||
|
delay*: float ##animation delay
|
||||||
|
PSoundRecord* = ref TSoundRecord
|
||||||
|
TSoundRecord* = object
|
||||||
|
file*: string
|
||||||
|
when defined(NoSFML):
|
||||||
|
contents*: TChecksumFile
|
||||||
|
else:
|
||||||
|
soundBuf*: PSoundBuffer
|
||||||
|
PSpriteSheet* = ref TSpriteSheet
|
||||||
|
TSpriteSheet* = object
|
||||||
|
file*: string
|
||||||
|
framew*,frameh*: int
|
||||||
|
rows*, cols*: int
|
||||||
|
when defined(NoSFML):
|
||||||
|
contents*: TChecksumFile
|
||||||
|
when not defined(NoSFML):
|
||||||
|
sprite*: PSprite
|
||||||
|
tex*: PTexture
|
||||||
|
TGameState* = enum
|
||||||
|
Lobby, Transitioning, Field
|
||||||
|
const
|
||||||
|
TAU* = PI * 2.0
|
||||||
|
MomentMult* = 0.62 ## global moment of inertia multiplier
|
||||||
|
var
|
||||||
|
cfg: PZoneSettings
|
||||||
|
SpriteSheets* = initTable[string, PSpriteSheet](64)
|
||||||
|
SoundCache * = initTable[string, PSoundRecord](64)
|
||||||
|
nameToVehID*: TTable[string, int]
|
||||||
|
nameToItemID*: TTable[string, int]
|
||||||
|
nameToObjID*: TTable[string, int]
|
||||||
|
nameToBulletID*: TTable[string, int]
|
||||||
|
activeState = Lobby
|
||||||
|
|
||||||
|
proc newSprite(filename: string; errors: var seq[string]): PSpriteSheet
|
||||||
|
proc load*(ss: PSpriteSheet): bool {.discardable.}
|
||||||
|
proc newSound(filename: string; errors: var seq[string]): PSoundRecord
|
||||||
|
proc load*(s: PSoundRecord): bool {.discardable.}
|
||||||
|
|
||||||
|
proc validateSettings*(settings: PJsonNode; errors: var seq[string]): bool
|
||||||
|
proc loadSettings*(rawJson: string, errors: var seq[string]): bool
|
||||||
|
proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool
|
||||||
|
|
||||||
|
proc fetchVeh*(name: string): PVehicleRecord
|
||||||
|
proc fetchItm*(itm: string): PItemRecord
|
||||||
|
proc fetchObj*(name: string): PObjectRecord
|
||||||
|
proc fetchBullet(name: string): PBulletRecord
|
||||||
|
|
||||||
|
proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings
|
||||||
|
proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord
|
||||||
|
proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord
|
||||||
|
proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord
|
||||||
|
proc importPhys(data: PJsonNode): TPhysicsRecord
|
||||||
|
proc importAnim(data: PJsonNode; errors: var seq[string]): PAnimationRecord
|
||||||
|
proc importHandling(data: PJsonNode): THandlingRecord
|
||||||
|
proc importBullet(data: PJsonNode; errors: var seq[string]): PBulletRecord
|
||||||
|
proc importSoul(data: PJsonNode): TSoulRecord
|
||||||
|
proc importExplosion(data: PJsonNode; errors: var seq[string]): TExplosionRecord
|
||||||
|
proc importSound(data: PJsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord
|
||||||
|
|
||||||
|
## this is the only pipe between lobby and main.nim
|
||||||
|
proc getActiveState*(): TGameState =
|
||||||
|
result = activeState
|
||||||
|
proc transition*() =
|
||||||
|
assert activeState == Lobby, "Transition() called from a state other than lobby!"
|
||||||
|
activeState = Transitioning
|
||||||
|
proc doneWithSaidTransition*() =
|
||||||
|
assert activeState == Transitioning, "Finished() called from a state other than transitioning!"
|
||||||
|
activeState = Field
|
||||||
|
|
||||||
|
|
||||||
|
proc checksumFile*(filename: string): TChecksumFile =
|
||||||
|
let fullText = readFile(filename)
|
||||||
|
result.unpackedSize = fullText.len
|
||||||
|
result.sum = toMD5(fullText)
|
||||||
|
result.compressed = compress(fullText)
|
||||||
|
proc checksumStr*(str: string): TChecksumFile =
|
||||||
|
result.unpackedSize = str.len
|
||||||
|
result.sum = toMD5(str)
|
||||||
|
result.compressed = compress(str)
|
||||||
|
|
||||||
|
|
||||||
|
##at this point none of these should ever be freed
|
||||||
|
proc free*(obj: PZoneSettings) =
|
||||||
|
echo "Free'd zone settings"
|
||||||
|
proc free*(obj: PSpriteSheet) =
|
||||||
|
echo "Free'd ", obj.file
|
||||||
|
proc free*(obj: PSoundRecord) =
|
||||||
|
echo "Free'd ", obj.file
|
||||||
|
|
||||||
|
proc loadAllAssets*() =
|
||||||
|
var
|
||||||
|
loaded = 0
|
||||||
|
failed = 0
|
||||||
|
for name, ss in SpriteSheets.pairs():
|
||||||
|
if load(ss):
|
||||||
|
inc loaded
|
||||||
|
else:
|
||||||
|
inc failed
|
||||||
|
echo loaded," sprites loaded. ", failed, " sprites failed."
|
||||||
|
loaded = 0
|
||||||
|
failed = 0
|
||||||
|
for name, s in SoundCache.pairs():
|
||||||
|
if load(s):
|
||||||
|
inc loaded
|
||||||
|
else:
|
||||||
|
inc failed
|
||||||
|
echo loaded, " sounds loaded. ", failed, " sounds failed."
|
||||||
|
proc getLevelSettings*(): PLevelSettings =
|
||||||
|
result = cfg.levelSettings
|
||||||
|
|
||||||
|
iterator playableVehicles*(): PVehicleRecord =
|
||||||
|
for v in cfg.vehicles.items():
|
||||||
|
if v.playable:
|
||||||
|
yield v
|
||||||
|
|
||||||
|
template allAssets*(body: stmt) {.dirty.}=
|
||||||
|
block:
|
||||||
|
var assetType = FGraphics
|
||||||
|
for file, asset in pairs(SpriteSheets):
|
||||||
|
body
|
||||||
|
assetType = FSound
|
||||||
|
for file, asset in pairs(SoundCache):
|
||||||
|
body
|
||||||
|
|
||||||
|
template cacheImpl(procName, cacheName, resultType: expr; body: stmt) {.dirty, immediate.} =
|
||||||
|
proc procName*(filename: string; errors: var seq[string]): resulttype =
|
||||||
|
if hasKey(cacheName, filename):
|
||||||
|
return cacheName[filename]
|
||||||
|
new(result, free)
|
||||||
|
body
|
||||||
|
cacheName[filename] = result
|
||||||
|
|
||||||
|
template checkFile(path: expr): stmt {.dirty, immediate.} =
|
||||||
|
if not existsFile(path):
|
||||||
|
errors.add("File missing: "& path)
|
||||||
|
|
||||||
|
cacheImpl newSprite, SpriteSheets, PSpriteSheet:
|
||||||
|
result.file = filename
|
||||||
|
if not(filename =~ re"\S+_(\d+)x(\d+)\.\S\S\S"):
|
||||||
|
errors.add "Bad file: "&filename&" must be in format name_WxH.png"
|
||||||
|
return
|
||||||
|
result.framew = strutils.parseInt(matches[0])
|
||||||
|
result.frameh = strutils.parseInt(matches[1])
|
||||||
|
checkFile("data/gfx"/result.file)
|
||||||
|
|
||||||
|
cacheImpl newSound, SoundCache, PSoundRecord:
|
||||||
|
result.file = filename
|
||||||
|
checkFile("data/sfx"/result.file)
|
||||||
|
|
||||||
|
proc expandPath*(assetType: TAssetType; fileName: string): string =
|
||||||
|
result = "data/"
|
||||||
|
case assetType
|
||||||
|
of FGraphics: result.add "gfx/"
|
||||||
|
of FSound: result.add "sfx/"
|
||||||
|
else: nil
|
||||||
|
result.add fileName
|
||||||
|
proc expandPath*(fc: ScFileChallenge): string {.inline.} =
|
||||||
|
result = expandPath(fc.assetType, fc.file)
|
||||||
|
|
||||||
|
when defined(NoSFML):
|
||||||
|
proc load*(ss: PSpriteSheet): bool =
|
||||||
|
if not ss.contents.unpackedSize == 0: return
|
||||||
|
ss.contents = checksumFile(expandPath(FGraphics, ss.file))
|
||||||
|
result = true
|
||||||
|
proc load*(s: PSoundRecord): bool =
|
||||||
|
if not s.contents.unpackedSize == 0: return
|
||||||
|
s.contents = checksumFile(expandPath(FSound, s.file))
|
||||||
|
result = true
|
||||||
|
else:
|
||||||
|
proc load*(ss: PSpriteSheet): bool =
|
||||||
|
if not ss.sprite.isNil:
|
||||||
|
return
|
||||||
|
var image = sfml.newImage("data/gfx/"/ss.file)
|
||||||
|
if image == nil:
|
||||||
|
echo "Image could not be loaded"
|
||||||
|
return
|
||||||
|
let size = image.getSize()
|
||||||
|
ss.rows = int(size.y / ss.frameh) #y is h
|
||||||
|
ss.cols = int(size.x / ss.framew) #x is w
|
||||||
|
ss.tex = newTexture(image)
|
||||||
|
image.destroy()
|
||||||
|
ss.sprite = newSprite()
|
||||||
|
ss.sprite.setTexture(ss.tex, true)
|
||||||
|
ss.sprite.setTextureRect(intrect(0, 0, ss.framew.cint, ss.frameh.cint))
|
||||||
|
ss.sprite.setOrigin(vec2f(ss.framew / 2, ss.frameh / 2))
|
||||||
|
result = true
|
||||||
|
proc load*(s: PSoundRecord): bool =
|
||||||
|
s.soundBuf = newSoundBuffer("data/sfx"/s.file)
|
||||||
|
if not s.soundBuf.isNil:
|
||||||
|
result = true
|
||||||
|
|
||||||
|
template addError(e: expr): stmt {.immediate.} =
|
||||||
|
errors.add(e)
|
||||||
|
result = false
|
||||||
|
proc validateSettings*(settings: PJsonNode, errors: var seq[string]): bool =
|
||||||
|
result = true
|
||||||
|
if settings.kind != JObject:
|
||||||
|
addError("Settings root must be an object")
|
||||||
|
return
|
||||||
|
if not settings.existsKey("vehicles"):
|
||||||
|
addError("Vehicles section missing")
|
||||||
|
if not settings.existsKey("objects"):
|
||||||
|
errors.add("Objects section is missing")
|
||||||
|
result = false
|
||||||
|
if not settings.existsKey("level"):
|
||||||
|
errors.add("Level settings section is missing")
|
||||||
|
result = false
|
||||||
|
else:
|
||||||
|
let lvl = settings["level"]
|
||||||
|
if lvl.kind != JObject or not lvl.existsKey("size"):
|
||||||
|
errors.add("Invalid level settings")
|
||||||
|
result = false
|
||||||
|
elif not lvl.existsKey("size") or lvl["size"].kind != JArray or lvl["size"].len != 2:
|
||||||
|
errors.add("Invalid/missing level size")
|
||||||
|
result = false
|
||||||
|
if not settings.existsKey("items"):
|
||||||
|
errors.add("Items section missing")
|
||||||
|
result = false
|
||||||
|
else:
|
||||||
|
let items = settings["items"]
|
||||||
|
if items.kind != JArray or items.len == 0:
|
||||||
|
errors.add "Invalid or empty item list"
|
||||||
|
else:
|
||||||
|
var id = 0
|
||||||
|
for i in items.items:
|
||||||
|
if i.kind != JArray: errors.add("Item #$1 is not an array"% $id)
|
||||||
|
elif i.len != 3: errors.add("($1) Item record should have 3 fields"%($id))
|
||||||
|
elif i[0].kind != JString or i[1].kind != JString or i[2].kind != JObject:
|
||||||
|
errors.add("($1) Item should be in form [name, type, {item: data}]"% $id)
|
||||||
|
result = false
|
||||||
|
inc id
|
||||||
|
|
||||||
|
proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool =
|
||||||
|
if not existsFile(filename):
|
||||||
|
errors.add("File does not exist: "&filename)
|
||||||
|
else:
|
||||||
|
result = loadSettings(readFile(filename), errors)
|
||||||
|
|
||||||
|
proc loadSettings*(rawJson: string, errors: var seq[string]): bool =
|
||||||
|
var settings: PJsonNode
|
||||||
|
try:
|
||||||
|
settings = parseJson(rawJson)
|
||||||
|
except EJsonParsingError:
|
||||||
|
errors.add("JSON parsing error: "& getCurrentExceptionMsg())
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
errors.add("Unknown exception: "& getCurrentExceptionMsg())
|
||||||
|
return
|
||||||
|
if not validateSettings(settings, errors):
|
||||||
|
return
|
||||||
|
if cfg != nil: #TODO try this
|
||||||
|
echo("Overwriting zone settings")
|
||||||
|
free(cfg)
|
||||||
|
cfg = nil
|
||||||
|
new(cfg, free)
|
||||||
|
cfg.levelSettings = importLevel(settings, errors)
|
||||||
|
cfg.vehicles = @[]
|
||||||
|
cfg.items = @[]
|
||||||
|
cfg.objects = @[]
|
||||||
|
cfg.bullets = @[]
|
||||||
|
nameToVehID = initTable[string, int](32)
|
||||||
|
nameToItemID = initTable[string, int](32)
|
||||||
|
nameToObjID = initTable[string, int](32)
|
||||||
|
nameToBulletID = initTable[string, int](32)
|
||||||
|
var
|
||||||
|
vID = 0'i16
|
||||||
|
bID = 0'i16
|
||||||
|
for vehicle in settings["vehicles"].items:
|
||||||
|
var veh = importVeh(vehicle, errors)
|
||||||
|
veh.id = vID
|
||||||
|
cfg.vehicles.add veh
|
||||||
|
nameToVehID[veh.name] = veh.id
|
||||||
|
inc vID
|
||||||
|
vID = 0
|
||||||
|
if settings.existsKey("bullets"):
|
||||||
|
for blt in settings["bullets"].items:
|
||||||
|
var bullet = importBullet(blt, errors)
|
||||||
|
bullet.id = bID
|
||||||
|
cfg.bullets.add bullet
|
||||||
|
nameToBulletID[bullet.name] = bullet.id
|
||||||
|
inc bID
|
||||||
|
for item in settings["items"].items:
|
||||||
|
var itm = importItem(item, errors)
|
||||||
|
itm.id = vID
|
||||||
|
cfg.items.add itm
|
||||||
|
nameToItemID[itm.name] = itm.id
|
||||||
|
inc vID
|
||||||
|
if itm.kind == Projectile:
|
||||||
|
if itm.bullet.isNil:
|
||||||
|
errors.add("Projectile #$1 has no bullet!"% $vID)
|
||||||
|
elif itm.bullet.id == -1:
|
||||||
|
## this item has an anonymous bullet, fix the ID and name
|
||||||
|
itm.bullet.id = bID
|
||||||
|
itm.bullet.name = itm.name
|
||||||
|
cfg.bullets.add itm.bullet
|
||||||
|
nameToBulletID[itm.bullet.name] = itm.bullet.id
|
||||||
|
inc bID
|
||||||
|
vID = 0
|
||||||
|
for obj in settings["objects"].items:
|
||||||
|
var o = importObject(obj, errors)
|
||||||
|
o.id = vID
|
||||||
|
cfg.objects.add o
|
||||||
|
nameToObjID[o.name] = o.id
|
||||||
|
inc vID
|
||||||
|
result = (errors.len == 0)
|
||||||
|
|
||||||
|
proc `$`*(obj: PSpriteSheet): string =
|
||||||
|
return "<Sprite $1 ($2x$3) $4 rows $5 cols>" % [obj.file, $obj.framew, $obj.frameh, $obj.rows, $obj.cols]
|
||||||
|
|
||||||
|
proc fetchVeh*(name: string): PVehicleRecord =
|
||||||
|
return cfg.vehicles[nameToVehID[name]]
|
||||||
|
proc fetchItm*(itm: string): PItemRecord =
|
||||||
|
return cfg.items[nameToItemID[itm]]
|
||||||
|
proc fetchObj*(name: string): PObjectRecord =
|
||||||
|
return cfg.objects[nameToObjID[name]]
|
||||||
|
proc fetchBullet(name: string): PBulletRecord =
|
||||||
|
return cfg.bullets[nameToBulletID[name]]
|
||||||
|
|
||||||
|
proc getField(node: PJsonNode, field: string, target: var float) =
|
||||||
|
if not node.existsKey(field):
|
||||||
|
return
|
||||||
|
if node[field].kind == JFloat:
|
||||||
|
target = node[field].fnum
|
||||||
|
elif node[field].kind == JInt:
|
||||||
|
target = node[field].num.float
|
||||||
|
proc getField(node: PJsonNode, field: string, target: var int) =
|
||||||
|
if not node.existsKey(field):
|
||||||
|
return
|
||||||
|
if node[field].kind == JInt:
|
||||||
|
target = node[field].num.int
|
||||||
|
elif node[field].kind == JFloat:
|
||||||
|
target = node[field].fnum.int
|
||||||
|
proc getField(node: PJsonNode; field: string; target: var bool) =
|
||||||
|
if not node.existsKey(field):
|
||||||
|
return
|
||||||
|
case node[field].kind
|
||||||
|
of JBool:
|
||||||
|
target = node[field].bval
|
||||||
|
of JInt:
|
||||||
|
target = (node[field].num != 0)
|
||||||
|
of JFloat:
|
||||||
|
target = (node[field].fnum != 0.0)
|
||||||
|
else: nil
|
||||||
|
|
||||||
|
template checkKey(node: expr; key: string): stmt =
|
||||||
|
if not existsKey(node, key):
|
||||||
|
return
|
||||||
|
|
||||||
|
proc importTrail(data: PJsonNode; errors: var seq[string]): TTrailRecord =
|
||||||
|
checkKey(data, "trail")
|
||||||
|
result.anim = importAnim(data["trail"], errors)
|
||||||
|
result.timer = 1000.0
|
||||||
|
getField(data["trail"], "timer", result.timer)
|
||||||
|
result.timer /= 1000.0
|
||||||
|
proc importLevel(data: PJsonNode; errors: var seq[string]): PLevelSettings =
|
||||||
|
new(result)
|
||||||
|
result.size = vec2i(5000, 5000)
|
||||||
|
result.starfield = @[]
|
||||||
|
|
||||||
|
checkKey(data, "level")
|
||||||
|
var level = data["level"]
|
||||||
|
if level.existsKey("size") and level["size"].kind == JArray and level["size"].len == 2:
|
||||||
|
result.size.x = level["size"][0].num.cint
|
||||||
|
result.size.y = level["size"][1].num.cint
|
||||||
|
if level.existsKey("starfield"):
|
||||||
|
for star in level["starfield"].items:
|
||||||
|
result.starfield.add(newSprite(star.str, errors))
|
||||||
|
proc importPhys(data: PJsonNode): TPhysicsRecord =
|
||||||
|
result.radius = 20.0
|
||||||
|
result.mass = 10.0
|
||||||
|
|
||||||
|
if data.existsKey("physics") and data["physics"].kind == JObject:
|
||||||
|
let phys = data["physics"]
|
||||||
|
phys.getField("radius", result.radius)
|
||||||
|
phys.getField("mass", result.mass)
|
||||||
|
when not defined(NoChipmunk):
|
||||||
|
result.moment = momentForCircle(result.mass, 0.0, result.radius, vectorZero) * MomentMult
|
||||||
|
proc importHandling(data: PJsonNode): THandlingRecord =
|
||||||
|
result.thrust = 45.0
|
||||||
|
result.topSpeed = 100.0 #unused
|
||||||
|
result.reverse = 30.0
|
||||||
|
result.strafe = 30.0
|
||||||
|
result.rotation = 2200.0
|
||||||
|
|
||||||
|
checkKey(data, "handling")
|
||||||
|
if data["handling"].kind != JObject:
|
||||||
|
return
|
||||||
|
|
||||||
|
let hand = data["handling"]
|
||||||
|
hand.getField("thrust", result.thrust)
|
||||||
|
hand.getField("top_speed", result.topSpeed)
|
||||||
|
hand.getField("reverse", result.reverse)
|
||||||
|
hand.getField("strafe", result.strafe)
|
||||||
|
hand.getField("rotation", result.rotation)
|
||||||
|
proc importAnim(data: PJsonNode, errors: var seq[string]): PAnimationRecord =
|
||||||
|
new(result)
|
||||||
|
result.angle = 0.0
|
||||||
|
result.delay = 1000.0
|
||||||
|
result.spriteSheet = nil
|
||||||
|
|
||||||
|
if data.existsKey("anim"):
|
||||||
|
let anim = data["anim"]
|
||||||
|
if anim.kind == JObject:
|
||||||
|
if anim.existsKey("file"):
|
||||||
|
result.spriteSheet = newSprite(anim["file"].str, errors)
|
||||||
|
|
||||||
|
anim.getField "angle", result.angle
|
||||||
|
anim.getField "delay", result.delay
|
||||||
|
elif data["anim"].kind == JString:
|
||||||
|
result.spriteSheet = newSprite(anim.str, errors)
|
||||||
|
|
||||||
|
result.angle = radians(result.angle) ## comes in as degrees
|
||||||
|
result.delay /= 1000 ## delay comes in as milliseconds
|
||||||
|
proc importSoul(data: PJsonNode): TSoulRecord =
|
||||||
|
result.energy = 10000
|
||||||
|
result.health = 1
|
||||||
|
checkKey(data, "soul")
|
||||||
|
let soul = data["soul"]
|
||||||
|
soul.getField("energy", result.energy)
|
||||||
|
soul.getField("health", result.health)
|
||||||
|
proc importExplosion(data: PJsonNode; errors: var seq[string]): TExplosionRecord =
|
||||||
|
checkKey(data, "explode")
|
||||||
|
let expl = data["explode"]
|
||||||
|
result.anim = importAnim(expl, errors)
|
||||||
|
result.sound = importSound(expl, errors, "sound")
|
||||||
|
proc importSound*(data: PJsonNode; errors: var seq[string]; fieldName: string = nil): PSoundRecord =
|
||||||
|
if data.kind == JObject:
|
||||||
|
checkKey(data, fieldName)
|
||||||
|
result = newSound(data[fieldName].str, errors)
|
||||||
|
elif data.kind == JString:
|
||||||
|
result = newSound(data.str, errors)
|
||||||
|
|
||||||
|
proc importVeh(data: PJsonNode; errors: var seq[string]): PVehicleRecord =
|
||||||
|
new(result)
|
||||||
|
result.playable = false
|
||||||
|
if data.kind != JArray or data.len != 2 or
|
||||||
|
(data.kind == JArray and
|
||||||
|
(data[0].kind != JString or data[1].kind != JObject)):
|
||||||
|
result.name = "(broken)"
|
||||||
|
errors.add "Vehicle record is malformed"
|
||||||
|
return
|
||||||
|
var vehData = data[1]
|
||||||
|
result.name = data[0].str
|
||||||
|
result.anim = importAnim(vehdata, errors)
|
||||||
|
result.physics = importPhys(vehdata)
|
||||||
|
result.handling = importHandling(vehdata)
|
||||||
|
vehdata.getField("playable", result.playable)
|
||||||
|
if result.anim.spriteSheet.isNil and result.playable:
|
||||||
|
result.playable = false
|
||||||
|
proc importObject(data: PJsonNode; errors: var seq[string]): PObjectRecord =
|
||||||
|
new(result)
|
||||||
|
if data.kind != JArray or data.len != 2:
|
||||||
|
result.name = "(broken)"
|
||||||
|
return
|
||||||
|
result.name = data[0].str
|
||||||
|
result.anim = importAnim(data[1], errors)
|
||||||
|
result.physics = importPhys(data[1])
|
||||||
|
proc importItem(data: PJsonNode; errors: var seq[string]): PItemRecord =
|
||||||
|
new(result)
|
||||||
|
if data.kind != JArray or data.len != 3:
|
||||||
|
result.name = "(broken)"
|
||||||
|
errors.add "Item record is malformed"
|
||||||
|
return
|
||||||
|
result.name = data[0].str
|
||||||
|
result.anim = importAnim(data[2], errors)
|
||||||
|
result.physics = importPhys(data[2])
|
||||||
|
|
||||||
|
result.cooldown = 100.0
|
||||||
|
data[2].getField("cooldown", result.cooldown)
|
||||||
|
result.cooldown /= 1000.0 ##cooldown is stored in ms
|
||||||
|
|
||||||
|
result.useSound = importSound(data[2], errors, "useSound")
|
||||||
|
|
||||||
|
case data[1].str.toLower
|
||||||
|
of "projectile":
|
||||||
|
result.kind = Projectile
|
||||||
|
if data[2]["bullet"].kind == JString:
|
||||||
|
result.bullet = fetchBullet(data[2]["bullet"].str)
|
||||||
|
elif data[2]["bullet"].kind == JInt:
|
||||||
|
result.bullet = cfg.bullets[data[2]["bullet"].num.int]
|
||||||
|
elif data[2]["bullet"].kind == JObject:
|
||||||
|
result.bullet = importBullet(data[2]["bullet"], errors)
|
||||||
|
else:
|
||||||
|
errors.add "UNKNOWN BULLET TYPE for item "& result.name
|
||||||
|
of "ammo":
|
||||||
|
result.kind = Ammo
|
||||||
|
of "utility":
|
||||||
|
nil
|
||||||
|
else:
|
||||||
|
errors.add "Invalid item type \""& data[1].str &"\" for item "& result.name
|
||||||
|
|
||||||
|
proc importBullet(data: PJsonNode; errors: var seq[string]): PBulletRecord =
|
||||||
|
new(result)
|
||||||
|
result.id = -1
|
||||||
|
|
||||||
|
var bdata: PJsonNode
|
||||||
|
if data.kind == JArray:
|
||||||
|
result.name = data[0].str
|
||||||
|
bdata = data[1]
|
||||||
|
elif data.kind == JObject:
|
||||||
|
bdata = data
|
||||||
|
else:
|
||||||
|
errors.add "Malformed bullet record"
|
||||||
|
return
|
||||||
|
|
||||||
|
result.anim = importAnim(bdata, errors)
|
||||||
|
result.physics = importPhys(bdata)
|
||||||
|
|
||||||
|
result.lifetime = 2000.0
|
||||||
|
result.inheritVelocity = 1000.0
|
||||||
|
result.baseVelocity = 30.0
|
||||||
|
getField(bdata, "lifetime", result.lifetime)
|
||||||
|
getField(bdata, "inheritVelocity", result.inheritVelocity)
|
||||||
|
getField(bdata, "baseVelocity", result.baseVelocity)
|
||||||
|
result.lifetime /= 1000.0 ## lifetime is stored as milliseconds
|
||||||
|
result.inheritVelocity /= 1000.0 ## inherit velocity 1000 = 1.0 (100%)
|
||||||
|
result.explosion = importExplosion(bdata, errors)
|
||||||
|
result.trail = importTrail(bdata, errors)
|
||||||
281
tests/manyloc/keineschweine/lib/sg_gui.nim
Normal file
281
tests/manyloc/keineschweine/lib/sg_gui.nim
Normal file
|
|
@ -0,0 +1,281 @@
|
||||||
|
import
|
||||||
|
sfml, sfml_colors,
|
||||||
|
input_helpers, sg_packets
|
||||||
|
from strutils import countlines
|
||||||
|
{.deadCodeElim: on.}
|
||||||
|
type
|
||||||
|
PGuiContainer* = ref TGuiContainer
|
||||||
|
TGuiContainer* = object of TObject
|
||||||
|
position: TVector2f
|
||||||
|
activeEntry: PTextEntry
|
||||||
|
widgets: seq[PGuiObject]
|
||||||
|
buttons: seq[PButton]
|
||||||
|
PGuiObject* = ref TGuiObject
|
||||||
|
TGuiObject* = object of TObject
|
||||||
|
PButton* = ref TButton
|
||||||
|
TButton* = object of TGuiObject
|
||||||
|
enabled: bool
|
||||||
|
bg*: sfml.PRectangleShape
|
||||||
|
text*: PText
|
||||||
|
onClick*: TButtonClicked
|
||||||
|
bounds: TFloatRect
|
||||||
|
PButtonCollection* = ref TButtonCollection
|
||||||
|
TButtonCollection* = object of TGuiContainer
|
||||||
|
PTextEntry* = ref TTextEntry
|
||||||
|
TTextEntry* = object of TButton
|
||||||
|
inputClient: input_helpers.PTextInput
|
||||||
|
PMessageArea* = ref TMessageArea
|
||||||
|
TMessageArea* = object of TGuiObject
|
||||||
|
pos: TVector2f
|
||||||
|
messages: seq[TMessage]
|
||||||
|
texts: seq[PText]
|
||||||
|
scrollBack*: int
|
||||||
|
sizeVisible*: int
|
||||||
|
direction*: int
|
||||||
|
TMessage = object
|
||||||
|
color: TColor
|
||||||
|
text: string
|
||||||
|
lines: int
|
||||||
|
TButtonClicked = proc(button: PButton)
|
||||||
|
var
|
||||||
|
guiFont* = newFont("data/fnt/LiberationMono-Regular.ttf")
|
||||||
|
messageProto* = newText("", guiFont, 16)
|
||||||
|
let
|
||||||
|
vectorZeroF* = vec2f(0.0, 0.0)
|
||||||
|
|
||||||
|
proc newGuiContainer*(): PGuiContainer
|
||||||
|
proc newGuiContainer*(pos: TVector2f): PGuiContainer {.inline.}
|
||||||
|
proc free*(container: PGuiContainer)
|
||||||
|
proc add*(container: PGuiContainer; widget: PGuiObject)
|
||||||
|
proc clearButtons*(container: PGuiContainer)
|
||||||
|
proc click*(container: PGuiContainer; position: TVector2f)
|
||||||
|
proc setActive*(container: PGuiContainer; entry: PTextEntry)
|
||||||
|
proc setPosition*(container: PGuiContainer; position: TVector2f)
|
||||||
|
|
||||||
|
proc update*(container: PGuiContainer; dt: float)
|
||||||
|
proc draw*(window: PRenderWindow; container: PGuiContainer) {.inline.}
|
||||||
|
|
||||||
|
proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea {.discardable.}
|
||||||
|
proc add*(m: PMessageArea; msg: ScChat)
|
||||||
|
|
||||||
|
proc draw*(window: PRenderWindow; b: PButton) {.inline.}
|
||||||
|
proc click*(b: PButton; p: TVector2f)
|
||||||
|
proc setPosition*(b: PButton; p: TVector2f)
|
||||||
|
proc setString*(b: PButton; s: string) {.inline.}
|
||||||
|
|
||||||
|
proc newButton*(container: PGuiContainer; text: string; position: TVector2f;
|
||||||
|
onClick: TButtonClicked; startEnabled: bool = true): PButton {.discardable.}
|
||||||
|
proc init(b: PButton; text: string; position: TVector2f; onClick: TButtonClicked)
|
||||||
|
proc setEnabled*(b: PButton; enabled: bool)
|
||||||
|
proc disable*(b: PButton) {.inline.}
|
||||||
|
proc enable*(b: PButton) {.inline.}
|
||||||
|
|
||||||
|
proc newTextEntry*(container: PGuiContainer; text: string;
|
||||||
|
position: TVector2f; onEnter: TInputFinishedProc = nil): PTextEntry {.discardable.}
|
||||||
|
proc init(t: PTextEntry; text: string; onEnter: TInputFinishedProc)
|
||||||
|
proc draw*(window: PRenderWindow, t: PTextEntry) {.inline.}
|
||||||
|
proc setActive*(t: PTextEntry) {.inline.}
|
||||||
|
proc clearText*(t: PTextEntry) {.inline.}
|
||||||
|
proc getText*(t: PTextEntry): string {.inline.}
|
||||||
|
|
||||||
|
proc update*(m: PMessageArea)
|
||||||
|
|
||||||
|
if guiFont == nil:
|
||||||
|
echo("Could not load font, crying softly to myself.")
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
proc newGuiContainer*(): PGuiContainer =
|
||||||
|
new(result, free)
|
||||||
|
result.widgets = @[]
|
||||||
|
result.buttons = @[]
|
||||||
|
proc newGuiContainer*(pos: TVector2f): PGuiContainer =
|
||||||
|
result = newGuiContainer()
|
||||||
|
result.setPosition pos
|
||||||
|
proc free*(container: PGuiContainer) =
|
||||||
|
container.widgets = nil
|
||||||
|
container.buttons = nil
|
||||||
|
proc add*(container: PGuiContainer; widget: PGuiObject) =
|
||||||
|
container.widgets.add(widget)
|
||||||
|
proc add*(container: PGuiContainer; button: PButton) =
|
||||||
|
if container.isNil: return
|
||||||
|
container.buttons.add(button)
|
||||||
|
proc clearButtons*(container: PGuiContainer) =
|
||||||
|
container.buttons.setLen 0
|
||||||
|
proc click*(container: PGuiContainer; position: TVector2f) =
|
||||||
|
for b in container.buttons:
|
||||||
|
click(b, position)
|
||||||
|
proc setActive*(container: PGuiContainer; entry: PTextEntry) =
|
||||||
|
container.activeEntry = entry
|
||||||
|
setActive(entry)
|
||||||
|
proc setPosition*(container: PGuiContainer; position: TVector2f) =
|
||||||
|
container.position = position
|
||||||
|
|
||||||
|
|
||||||
|
proc update*(container: PGuiContainer; dt: float) =
|
||||||
|
if not container.activeEntry.isNil:
|
||||||
|
container.activeEntry.setString(container.activeEntry.getText())
|
||||||
|
proc draw*(window: PRenderWindow; container: PGuiContainer) =
|
||||||
|
for b in container.buttons:
|
||||||
|
window.draw b
|
||||||
|
|
||||||
|
proc free(c: PButton) =
|
||||||
|
c.bg.destroy()
|
||||||
|
c.text.destroy()
|
||||||
|
c.bg = nil
|
||||||
|
c.text = nil
|
||||||
|
c.onClick = nil
|
||||||
|
proc newButton*(container: PGuiContainer; text: string;
|
||||||
|
position: TVector2f; onClick: TButtonClicked;
|
||||||
|
startEnabled: bool = true): PButton =
|
||||||
|
new(result, free)
|
||||||
|
init(result,
|
||||||
|
text,
|
||||||
|
if not container.isNil: position + container.position else: position,
|
||||||
|
onClick)
|
||||||
|
container.add result
|
||||||
|
if not startEnabled: disable(result)
|
||||||
|
|
||||||
|
proc init(b: PButton; text: string; position: TVector2f; onClick: TButtonClicked) =
|
||||||
|
b.bg = newRectangleShape()
|
||||||
|
b.bg.setSize(vec2f(80.0, 16.0))
|
||||||
|
b.bg.setFillColor(color(20, 30, 15))
|
||||||
|
b.text = newText(text, guiFont, 16)
|
||||||
|
b.onClick = onClick
|
||||||
|
b.setPosition(position)
|
||||||
|
b.enabled = true
|
||||||
|
proc copy*(c: PButton): PButton =
|
||||||
|
new(result, free)
|
||||||
|
result.bg = c.bg.copy()
|
||||||
|
result.text = c.text.copy()
|
||||||
|
result.onClick = c.onClick
|
||||||
|
result.setPosition(result.bg.getPosition())
|
||||||
|
|
||||||
|
proc setEnabled*(b: PButton; enabled: bool) =
|
||||||
|
b.enabled = enabled
|
||||||
|
if enabled:
|
||||||
|
b.text.setColor(White)
|
||||||
|
else:
|
||||||
|
b.text.setColor(Gray)
|
||||||
|
proc enable*(b: PButton) = setEnabled(b, true)
|
||||||
|
proc disable*(b: PButton) = setEnabled(b, false)
|
||||||
|
|
||||||
|
proc draw*(window: PRenderWindow; b: PButton) =
|
||||||
|
window.draw b.bg
|
||||||
|
window.draw b.text
|
||||||
|
proc setPosition*(b: PButton, p: TVector2f) =
|
||||||
|
b.bg.setPosition(p)
|
||||||
|
b.text.setPosition(p)
|
||||||
|
b.bounds = b.text.getGlobalBounds()
|
||||||
|
proc setString*(b: PButton; s: string) =
|
||||||
|
b.text.setString(s)
|
||||||
|
proc click*(b: PButton, p: TVector2f) =
|
||||||
|
if b.enabled and (addr b.bounds).contains(p.x, p.y):
|
||||||
|
b.onClick(b)
|
||||||
|
|
||||||
|
proc free(obj: PTextEntry) =
|
||||||
|
free(PButton(obj))
|
||||||
|
proc newTextEntry*(container: PGuiContainer; text: string;
|
||||||
|
position: TVector2F; onEnter: TInputFinishedProc = nil): PTextEntry =
|
||||||
|
new(result, free)
|
||||||
|
init(PButton(result), text, position + container.position, proc(b: PButton) =
|
||||||
|
setActive(container, PTextEntry(b)))
|
||||||
|
init(result, text, onEnter)
|
||||||
|
container.add result
|
||||||
|
proc init(t: PTextEntry; text: string; onEnter: TInputFinishedProc) =
|
||||||
|
t.inputClient = newTextInput(text, text.len, onEnter)
|
||||||
|
proc draw(window: PRenderWindow; t: PTextEntry) =
|
||||||
|
window.draw PButton(t)
|
||||||
|
proc clearText*(t: PTextEntry) =
|
||||||
|
t.inputClient.clear()
|
||||||
|
proc getText*(t: PTextEntry): string =
|
||||||
|
return t.inputClient.text
|
||||||
|
proc setActive*(t: PTextEntry) =
|
||||||
|
if not t.isNil and not t.inputClient.isNil:
|
||||||
|
input_helpers.setActive(t.inputClient)
|
||||||
|
|
||||||
|
|
||||||
|
discard """proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea =
|
||||||
|
new(result)
|
||||||
|
result.messages = @[]
|
||||||
|
result.pos = position
|
||||||
|
container.add(result)
|
||||||
|
proc add*(m: PMessageArea, text: string): PText =
|
||||||
|
result = messageProto.copy()
|
||||||
|
result.setString(text)
|
||||||
|
m.messages.add(result)
|
||||||
|
let nmsgs = len(m.messages)
|
||||||
|
var pos = vec2f(m.pos.x, m.pos.y)
|
||||||
|
for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)):
|
||||||
|
setPosition(m.messages[i], pos)
|
||||||
|
pos.y -= 16.0
|
||||||
|
|
||||||
|
proc draw*(window: PRenderWindow; m: PMessageArea) =
|
||||||
|
let nmsgs = len(m.messages)
|
||||||
|
if nmsgs == 0: return
|
||||||
|
for i in countdown(nmsgs - 1, max(nmsgs - 30, 0)):
|
||||||
|
window.draw(m.messages[i])
|
||||||
|
"""
|
||||||
|
proc newMessageArea*(container: PGuiContainer; position: TVector2f): PMessageArea =
|
||||||
|
new(result)
|
||||||
|
result.messages = @[]
|
||||||
|
result.texts = @[]
|
||||||
|
result.pos = position + container.position
|
||||||
|
result.sizeVisible = 10
|
||||||
|
result.scrollBack = 0
|
||||||
|
result.direction = -1 ## to push old messages up
|
||||||
|
container.add(result)
|
||||||
|
|
||||||
|
proc add*(m: PMessageArea, msg: ScChat) =
|
||||||
|
const prependName = {CPub, CPriv}
|
||||||
|
var mmm: TMessage
|
||||||
|
if msg.kind in prependName:
|
||||||
|
mmm.text = "<"
|
||||||
|
mmm.text.add msg.fromPlayer
|
||||||
|
mmm.text.add "> "
|
||||||
|
mmm.text.add msg.text
|
||||||
|
else:
|
||||||
|
mmm.text = msg.text
|
||||||
|
case msg.kind
|
||||||
|
of CPub: mmm.color = RoyalBlue
|
||||||
|
of CPriv, CSystem: mmm.color = Green
|
||||||
|
of CError: mmm.color = Red
|
||||||
|
|
||||||
|
mmm.lines = countLines(mmm.text)+1
|
||||||
|
|
||||||
|
m.messages.add mmm
|
||||||
|
update m
|
||||||
|
proc add*(m: PMessageArea, msg: string) {.inline.} =
|
||||||
|
var chat = newScChat(kind = CSystem, text = msg)
|
||||||
|
add(m, chat)
|
||||||
|
|
||||||
|
proc proctor*(m: PText; msg: ptr TMessage; pos: ptr TVector2f) =
|
||||||
|
m.setString msg.text
|
||||||
|
m.setColor msg.color
|
||||||
|
m.setPosition pos[]
|
||||||
|
proc update*(m: PMessageArea) =
|
||||||
|
if m.texts.len < m.sizeVisible:
|
||||||
|
echo "adding ", m.sizeVisible - m.texts.len, " fields"
|
||||||
|
for i in 1..m.sizeVisible - m.texts.len:
|
||||||
|
var t = messageProto.copy()
|
||||||
|
m.texts.add messageProto.copy()
|
||||||
|
elif m.texts.len > m.sizeVisible:
|
||||||
|
echo "cutting ", m.texts.len - m.sizeVisible, " fields"
|
||||||
|
for i in m.sizeVisible.. < m.texts.len:
|
||||||
|
m.texts.pop().destroy()
|
||||||
|
let nmsgs = m.messages.len()
|
||||||
|
if m.sizeVisible == 0 or nmsgs == 0:
|
||||||
|
echo "no messages? ", m.sizeVisible, ", ", nmsgs
|
||||||
|
return
|
||||||
|
var pos = vec2f(m.pos.x, m.pos.y)
|
||||||
|
for i in 0.. min(m.sizeVisible, nmsgs)-1:
|
||||||
|
##echo nmsgs - i - 1 - m.scrollBack
|
||||||
|
let msg = addr m.messages[nmsgs - i - 1 - m.scrollBack]
|
||||||
|
proctor(m.texts[i], msg, addr pos)
|
||||||
|
pos.y += (16 * m.direction * msg.lines).cfloat
|
||||||
|
|
||||||
|
proc draw*(window: PRenderWindow; m: PMessageArea) =
|
||||||
|
let nmsgs = len(m.texts)
|
||||||
|
if nmsgs == 0: return
|
||||||
|
for i in countdown(nmsgs - 1, max(nmsgs - m.sizeVisible, 0)):
|
||||||
|
window.draw m.texts[i]
|
||||||
|
|
||||||
106
tests/manyloc/keineschweine/lib/sg_packets.nim
Normal file
106
tests/manyloc/keineschweine/lib/sg_packets.nim
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
import genpacket_enet, sockets, md5, enet
|
||||||
|
defPacketImports()
|
||||||
|
|
||||||
|
type
|
||||||
|
PacketID* = char
|
||||||
|
|
||||||
|
template idpacket(pktName, id, s2c, c2s: expr): stmt {.immediate, dirty.} =
|
||||||
|
let `H pktName`* {.inject.} = id
|
||||||
|
defPacket(`Sc pktName`, s2c)
|
||||||
|
defPacket(`Cs pktName`, c2s)
|
||||||
|
|
||||||
|
forwardPacketT(Uint8, int8)
|
||||||
|
forwardPacketT(Uint16, int16)
|
||||||
|
forwardPacketT(TPort, int16)
|
||||||
|
|
||||||
|
idPacket(Login, 'a',
|
||||||
|
tuple[id: int32; alias: string; sessionKey: string],
|
||||||
|
tuple[alias: string, passwd: string])
|
||||||
|
|
||||||
|
let HZoneJoinReq* = 'j'
|
||||||
|
defPacket(CsZoneJoinReq, tuple[session: ScLogin])
|
||||||
|
|
||||||
|
defPacket(ScZoneRecord, tuple[
|
||||||
|
name: string = "", desc: string = "",
|
||||||
|
ip: string = "", port: TPort = 0.Tport])
|
||||||
|
idPacket(ZoneList, 'z',
|
||||||
|
tuple[network: string = "", zones: seq[ScZoneRecord]],
|
||||||
|
tuple[time: string])
|
||||||
|
|
||||||
|
let HPoing* = 'p'
|
||||||
|
defPacket(Poing, tuple[id: int32, time: float32])
|
||||||
|
|
||||||
|
type ChatType* = enum
|
||||||
|
CPub = 0'i8, CPriv, CSystem, CError
|
||||||
|
forwardPacketT(ChatType, int8)
|
||||||
|
idPacket(Chat, 'C',
|
||||||
|
tuple[kind: ChatType = CPub; fromPlayer: string = ""; text: string = ""],
|
||||||
|
tuple[target: string = ""; text: string = ""])
|
||||||
|
|
||||||
|
idPacket(Hello, 'h',
|
||||||
|
tuple[resp: string],
|
||||||
|
tuple[i: int8 = 14])
|
||||||
|
|
||||||
|
let HPlayerList* = 'P'
|
||||||
|
defPacket(ScPlayerRec, tuple[id: int32; alias: string = ""])
|
||||||
|
defPacket(ScPlayerList, tuple[players: seq[ScPlayerRec]])
|
||||||
|
|
||||||
|
let HTeamList* = 'T'
|
||||||
|
defPacket(ScTeam, tuple[id: int8; name: string = ""])
|
||||||
|
defPacket(ScTeamList, tuple[teams: seq[ScTeam]])
|
||||||
|
let HTeamChange* = 't'
|
||||||
|
|
||||||
|
idPacket(ZoneQuery, 'Q',
|
||||||
|
tuple[playerCount: Uint16], ##i should include a time here or something
|
||||||
|
tuple[pad: char = '\0'])
|
||||||
|
|
||||||
|
type SpawnKind = enum
|
||||||
|
SpawnItem = 1'i8, SpawnVehicle, SpawnObject
|
||||||
|
forwardPacketT(SpawnKind, int8)
|
||||||
|
defPacket(ScSpawn, tuple[
|
||||||
|
kind: SpawnKind; id: uint16; record: uint16; amount: uint16])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
type TAssetType* = enum
|
||||||
|
FZoneCfg = 1'i8, FGraphics, FSound
|
||||||
|
|
||||||
|
forwardPacketT(TAssetType, int8)
|
||||||
|
forwardPacket(MD5Digest, array[0..15, int8])
|
||||||
|
|
||||||
|
idPacket(FileChallenge, 'F',
|
||||||
|
tuple[file: string; assetType: TAssetType; fullLen: int32],
|
||||||
|
tuple[needFile: bool; checksum: MD5Digest])
|
||||||
|
|
||||||
|
|
||||||
|
let HChallengeResult* = '('
|
||||||
|
defPacket(ScChallengeResult, tuple[status: bool])
|
||||||
|
|
||||||
|
let HFileTransfer* = 'f'
|
||||||
|
defPacket(ScFileTransfer, tuple[fileSize: int32; pos: int32; data: string])
|
||||||
|
defPacket(CsFilepartAck, tuple[lastpos: int32])
|
||||||
|
|
||||||
|
##dir server messages
|
||||||
|
let HZoneLogin* = 'u'
|
||||||
|
defPacket(SdZoneLogin, tuple[name: string; key: string; record: ScZoneRecord])
|
||||||
|
defPacket(DsZoneLogin, tuple[status: bool])
|
||||||
|
let HDsMsg* = 'c'
|
||||||
|
defPacket(DsMsg, tuple[msg: string])
|
||||||
|
let HVerifyClient* = 'v'
|
||||||
|
defPacket(SdVerifyClient, tuple[session: ScLogin])
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
|
||||||
|
var buf = newBuffer(100)
|
||||||
|
var m = toMd5("hello there")
|
||||||
|
echo(repr(m))
|
||||||
|
buf.pack m
|
||||||
|
|
||||||
|
echo(repr(buf.data))
|
||||||
|
echo(len(buf.data))
|
||||||
|
|
||||||
|
buf.reset()
|
||||||
|
|
||||||
|
var x = buf.readMD5Digest()
|
||||||
|
echo(repr(x))
|
||||||
38
tests/manyloc/keineschweine/lib/sound_buffer.nim
Normal file
38
tests/manyloc/keineschweine/lib/sound_buffer.nim
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
when defined(NoSFML) or defined(NoChipmunk):
|
||||||
|
{.error.}
|
||||||
|
import sfml_audio, sfml_stuff, sg_assets, chipmunk
|
||||||
|
const
|
||||||
|
MinDistance* = 350.0
|
||||||
|
Attenuation* = 20.0
|
||||||
|
var
|
||||||
|
liveSounds: seq[PSound] = @[]
|
||||||
|
deadSounds: seq[PSound] = @[]
|
||||||
|
|
||||||
|
proc playSound*(sound: PSoundRecord, pos: TVector) =
|
||||||
|
if sound.isNil or sound.soundBuf.isNil: return
|
||||||
|
var s: PSound
|
||||||
|
if deadSounds.len == 0:
|
||||||
|
s = sfml_audio.newSound()
|
||||||
|
s.setLoop false
|
||||||
|
s.setRelativeToListener true
|
||||||
|
s.setAttenuation Attenuation
|
||||||
|
s.setMinDistance MinDistance
|
||||||
|
else:
|
||||||
|
s = deadSounds.pop()
|
||||||
|
s.setPosition(vec3f(pos.x, 0, pos.y))
|
||||||
|
s.setBuffer(sound.soundBuf)
|
||||||
|
s.play()
|
||||||
|
liveSounds.add s
|
||||||
|
|
||||||
|
proc updateSoundBuffer*() =
|
||||||
|
var i = 0
|
||||||
|
while i < len(liveSounds):
|
||||||
|
if liveSounds[i].getStatus == Stopped:
|
||||||
|
deadSounds.add liveSounds[i]
|
||||||
|
liveSounds.del i
|
||||||
|
else:
|
||||||
|
inc i
|
||||||
|
|
||||||
|
proc report*() =
|
||||||
|
echo "live: ", liveSounds.len
|
||||||
|
echo "dead: ", deadSounds.len
|
||||||
35
tests/manyloc/keineschweine/lib/vehicles.nim
Normal file
35
tests/manyloc/keineschweine/lib/vehicles.nim
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import
|
||||||
|
sfml, chipmunk,
|
||||||
|
sg_assets, sfml_stuff, keineschweine
|
||||||
|
|
||||||
|
|
||||||
|
proc accel*(obj: PVehicle, dt: float) =
|
||||||
|
#obj.velocity += vec2f(
|
||||||
|
# cos(obj.angle) * obj.record.handling.thrust.float * dt,
|
||||||
|
# sin(obj.angle) * obj.record.handling.thrust.float * dt)
|
||||||
|
obj.body.applyImpulse(
|
||||||
|
vectorForAngle(obj.body.getAngle()) * dt * obj.record.handling.thrust,
|
||||||
|
vectorZero)
|
||||||
|
proc reverse*(obj: PVehicle, dt: float) =
|
||||||
|
#obj.velocity += vec2f(
|
||||||
|
# -cos(obj.angle) * obj.record.handling.reverse.float * dt,
|
||||||
|
# -sin(obj.angle) * obj.record.handling.reverse.float * dt)
|
||||||
|
obj.body.applyImpulse(
|
||||||
|
-vectorForAngle(obj.body.getAngle()) * dt * obj.record.handling.reverse,
|
||||||
|
vectorZero)
|
||||||
|
proc strafe_left*(obj: PVehicle, dt: float) =
|
||||||
|
obj.body.applyImpulse(
|
||||||
|
vectorForAngle(obj.body.getAngle()).perp() * obj.record.handling.strafe * dt,
|
||||||
|
vectorZero)
|
||||||
|
proc strafe_right*(obj: PVehicle, dt: float) =
|
||||||
|
obj.body.applyImpulse(
|
||||||
|
vectorForAngle(obj.body.getAngle()).rperp()* obj.record.handling.strafe * dt,
|
||||||
|
vectorZero)
|
||||||
|
proc turn_right*(obj: PVehicle, dt: float) =
|
||||||
|
#obj.angle = (obj.angle + (obj.record.handling.rotation.float / 10.0 * dt)) mod TAU
|
||||||
|
obj.body.setTorque(obj.record.handling.rotation)
|
||||||
|
proc turn_left*(obj: PVehicle, dt: float) =
|
||||||
|
#obj.angle = (obj.angle - (obj.record.handling.rotation.float / 10.0 * dt)) mod TAU
|
||||||
|
obj.body.setTorque(-obj.record.handling.rotation)
|
||||||
|
proc offsetAngle*(obj: PVehicle): float {.inline.} =
|
||||||
|
return (obj.record.anim.angle + obj.body.getAngle())
|
||||||
40
tests/manyloc/keineschweine/lib/zlib_helpers.nim
Normal file
40
tests/manyloc/keineschweine/lib/zlib_helpers.nim
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import zlib
|
||||||
|
|
||||||
|
proc compress*(source: string): string =
|
||||||
|
var
|
||||||
|
sourcelen = source.len
|
||||||
|
destlen = sourcelen + (sourcelen.float * 0.1).int + 16
|
||||||
|
result = ""
|
||||||
|
result.setLen destLen
|
||||||
|
var res = zlib.compress(cstring(result), addr destLen, cstring(source), sourceLen)
|
||||||
|
if res != Z_OK:
|
||||||
|
echo "Error occured: ", res
|
||||||
|
elif destLen < result.len:
|
||||||
|
result.setLen(destLen)
|
||||||
|
|
||||||
|
proc uncompress*(source: string, destLen: var int): string =
|
||||||
|
result = ""
|
||||||
|
result.setLen destLen
|
||||||
|
var res = zlib.uncompress(cstring(result), addr destLen, cstring(source), source.len)
|
||||||
|
if res != Z_OK:
|
||||||
|
echo "Error occured: ", res
|
||||||
|
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
import strutils
|
||||||
|
var r = compress("Hello")
|
||||||
|
echo repr(r)
|
||||||
|
var l = "Hello".len
|
||||||
|
var rr = uncompress(r, l)
|
||||||
|
echo repr(rr)
|
||||||
|
assert rr == "Hello"
|
||||||
|
|
||||||
|
proc `*`(a: string; b: int): string {.inline.} = result = repeatStr(b, a)
|
||||||
|
var s = "yo dude sup bruh homie" * 50
|
||||||
|
r = compress(s)
|
||||||
|
echo s.len, " -> ", r.len
|
||||||
|
|
||||||
|
l = s.len
|
||||||
|
rr = uncompress(r, l)
|
||||||
|
echo r.len, " -> ", rr.len
|
||||||
|
assert rr == s
|
||||||
155
tests/manyloc/keineschweine/nakefile.nim
Normal file
155
tests/manyloc/keineschweine/nakefile.nim
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
import nake
|
||||||
|
import httpclient, zipfiles, times, math
|
||||||
|
nakeImports
|
||||||
|
|
||||||
|
randomize()
|
||||||
|
|
||||||
|
const
|
||||||
|
GameAssets = "http://dl.dropbox.com/u/37533467/data-08-01-2012.7z"
|
||||||
|
BinLibs = "http://dl.dropbox.com/u/37533467/libs-2012-09-12.zip"
|
||||||
|
ExeName = "keineschweine"
|
||||||
|
ServerDefines = "-d:NoSFML -d:NoChipmunk"
|
||||||
|
TestBuildDefines = "-d:escapeMenuTest -d:debugWeps -d:showFPS -d:moreNimrod -d:debugKeys -d:foo -d:recordMode --forceBuild"
|
||||||
|
ReleaseDefines = "-d:release --deadCodeElim:on"
|
||||||
|
ReleaseTestDefines = "-d:debugWeps -d:debugKeys --forceBuild"
|
||||||
|
|
||||||
|
task "testprofile", "..":
|
||||||
|
if shell("nimrod", TestBuildDefines, "--profiler:on", "--stacktrace:on", "compile", ExeName) == 0:
|
||||||
|
shell "."/ExeName, "offline"
|
||||||
|
|
||||||
|
task "test", "Build with test defines":
|
||||||
|
if shell("nimrod", TestBuildDefines, "compile", ExeName) != 0:
|
||||||
|
quit "The build failed."
|
||||||
|
|
||||||
|
task "testrun", "Build with test defines and run":
|
||||||
|
runTask "test"
|
||||||
|
shell "."/ExeName
|
||||||
|
|
||||||
|
task "test2", "Build release test build test release build":
|
||||||
|
if shell("nimrod", ReleaseDefines, ReleaseTestDefines, "compile", ExeName) == 0:
|
||||||
|
shell "."/ExeName
|
||||||
|
|
||||||
|
discard """task "dirserver", "build the directory server":
|
||||||
|
withDir "server":
|
||||||
|
if shell("nimrod", ServerDefines, "compile", "dirserver") != 0:
|
||||||
|
echo "Failed to build the dirserver"
|
||||||
|
quit 1"""
|
||||||
|
|
||||||
|
task "zoneserver", "build the zone server":
|
||||||
|
withDir "enet_server":
|
||||||
|
if shell("nimrod", ServerDefines, "compile", "enet_server") != 0:
|
||||||
|
quit "Failed to build the zoneserver"
|
||||||
|
task "zoneserver-gui", "build the zone server, with gui!":
|
||||||
|
withDir "enet_server":
|
||||||
|
if shell("nimrod", ServerDefines, "--app:gui", "compile", "enet_server") != 0:
|
||||||
|
quit "Failed to build the zoneserver"
|
||||||
|
|
||||||
|
task "servers", "build the server and directory server":
|
||||||
|
#runTask "dirserver"
|
||||||
|
runTask "zoneserver"
|
||||||
|
echo "Successfully built both servers :')"
|
||||||
|
|
||||||
|
task "all", "run SERVERS and TEST tasks":
|
||||||
|
runTask "servers"
|
||||||
|
runTask "test"
|
||||||
|
|
||||||
|
task "release", "release build":
|
||||||
|
let res = shell("nimrod", ReleaseDefines, "compile", ExeName)
|
||||||
|
if res != 0:
|
||||||
|
echo "The build failed."
|
||||||
|
quit 1
|
||||||
|
else:
|
||||||
|
runTask "clean"
|
||||||
|
## zip up all the files and such or something useful here
|
||||||
|
|
||||||
|
task "testskel", "create skeleton test dir for testing":
|
||||||
|
let dirname = "test-"& $random(5000)
|
||||||
|
removeDir dirName
|
||||||
|
createDir dirName/"data/fnt"
|
||||||
|
copyFile "data/fnt/LiberationMono-Regular", dirName/"data/fnt/LiberationMono-Regular.ttf"
|
||||||
|
copyFile "client_settings.json", dirName/"client_settings.json"
|
||||||
|
runTask "test"
|
||||||
|
copyFile ExeName, dirName/ExeName
|
||||||
|
withDir dirName:
|
||||||
|
shell "."/ExeName
|
||||||
|
|
||||||
|
|
||||||
|
task "clean", "cleanup generated files":
|
||||||
|
var dirs = @["nimcache", "server"/"nimcache"]
|
||||||
|
dirs.each(proc(x: var string) =
|
||||||
|
if existsDir(x): removeDir(x))
|
||||||
|
|
||||||
|
task "download", "download game assets":
|
||||||
|
var
|
||||||
|
skipAssets = false
|
||||||
|
path = expandFilename("data")
|
||||||
|
path.add DirSep
|
||||||
|
path.add(extractFilename(gameAssets))
|
||||||
|
if existsFile(path):
|
||||||
|
echo "The file already exists\n",
|
||||||
|
"[R]emove [M]ove [Q]uit [S]kip Source: ", GameAssets
|
||||||
|
case stdin.readLine.toLower
|
||||||
|
of "r":
|
||||||
|
removeFile path
|
||||||
|
of "m":
|
||||||
|
moveFile path, path/../(extractFilename(gameAssets)&"-old")
|
||||||
|
of "s":
|
||||||
|
skipAssets = true
|
||||||
|
else:
|
||||||
|
quit 0
|
||||||
|
else:
|
||||||
|
echo "Downloading from ", GameAssets
|
||||||
|
if not skipAssets:
|
||||||
|
echo "Downloading to ", path
|
||||||
|
downloadFile gameAssets, path
|
||||||
|
echo "Download finished"
|
||||||
|
|
||||||
|
let targetDir = parentDir(parentDir(path))
|
||||||
|
when defined(linux):
|
||||||
|
let z7 = findExe("7z")
|
||||||
|
if z7 == "":
|
||||||
|
echo "Could not find 7z"
|
||||||
|
elif shell(z7, "t", path) != 0: ##note to self: make sure this is right
|
||||||
|
echo "Bad download"
|
||||||
|
else:
|
||||||
|
echo "Unpacking..."
|
||||||
|
shell(z7, "x", "-w[$1]" % targetDir, path)
|
||||||
|
else:
|
||||||
|
echo "I do not know how to unpack the data on this system. Perhaps you could ",
|
||||||
|
"fill this part in?"
|
||||||
|
|
||||||
|
echo "Download binary libs? Only libs for linux are available currently, enjoy the irony.\n",
|
||||||
|
"[Y]es [N]o Source: ", BinLibs
|
||||||
|
case stdin.readline.toLower
|
||||||
|
of "y", "yes":
|
||||||
|
discard ## o_O
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
path = extractFilename(BinLibs)
|
||||||
|
downloadFile BinLibs, path
|
||||||
|
echo "Downloaded dem libs ", path
|
||||||
|
when true: echo "Unpack it yourself, sorry."
|
||||||
|
else: ## this crashes, dunno why
|
||||||
|
var
|
||||||
|
z: TZipArchive
|
||||||
|
destDir = getCurrentDir()/("unzip"& $random(5000))
|
||||||
|
if not z.open(path, fmRead):
|
||||||
|
echo "Could not open zip, bad download?"
|
||||||
|
return
|
||||||
|
echo "Extracting to ", destDir
|
||||||
|
createDir destDir
|
||||||
|
#z.extractAll destDir
|
||||||
|
for f in z.walkFiles():
|
||||||
|
z.extractFile(f, destDir/f)
|
||||||
|
z.close()
|
||||||
|
echo "Extracted the libs dir. Copy the ones you need to this dir."
|
||||||
|
|
||||||
|
task "zip-lib", "zip up the libs dir":
|
||||||
|
var z: TZipArchive
|
||||||
|
if not z.open("libs-"& getDateStr() &".zip", fmReadWrite):
|
||||||
|
quit "Could not open zip"
|
||||||
|
for file in walkDirRec("libs", {pcFile, pcDir}):
|
||||||
|
echo "adding file ", file
|
||||||
|
z.addFile(file)
|
||||||
|
z.close()
|
||||||
|
echo "Great success!"
|
||||||
1
tests/manyloc/keineschweine/nakefile.nimrod.cfg
Normal file
1
tests/manyloc/keineschweine/nakefile.nimrod.cfg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
path = "dependencies/nake"
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"network":"lamenet",
|
||||||
|
"port":2049,
|
||||||
|
"zones":[
|
||||||
|
{"name":"alphazone","key":"skittles"}
|
||||||
|
]
|
||||||
|
}
|
||||||
6
tests/manyloc/keineschweine/server/nimrod.cfg
Normal file
6
tests/manyloc/keineschweine/server/nimrod.cfg
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
debugger = off
|
||||||
|
deadCodeElim = on
|
||||||
|
path = ".."
|
||||||
|
path = "../genpacket"
|
||||||
|
path = "../helpers"
|
||||||
|
define = NoSFML
|
||||||
201
tests/manyloc/keineschweine/server/old_dirserver.nim
Normal file
201
tests/manyloc/keineschweine/server/old_dirserver.nim
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
## directory server
|
||||||
|
## handles client authorization and assets
|
||||||
|
|
||||||
|
import
|
||||||
|
sockets, times, streams, streams_enh, tables, json, os,
|
||||||
|
sg_packets, sg_assets, md5, server_utils, map_filter
|
||||||
|
type
|
||||||
|
THandler = proc(client: PCLient; stream: PStream)
|
||||||
|
var
|
||||||
|
server: TSocket
|
||||||
|
handlers = initTable[char, THandler](16)
|
||||||
|
thisZone = newScZoneRecord("local", "sup")
|
||||||
|
zoneList = newScZoneList()
|
||||||
|
thisZoneSettings: string
|
||||||
|
zoneSlots: seq[tuple[name: string; key: string]] = @[]
|
||||||
|
zones: seq[PClient] = @[]
|
||||||
|
## I was high.
|
||||||
|
clients = initTable[TupAddress, PClient](16)
|
||||||
|
alias2client = initTable[string, PClient](32)
|
||||||
|
allClients: seq[PClient] = @[]
|
||||||
|
|
||||||
|
proc findClient*(host: string; port: int16): PClient =
|
||||||
|
let addy: TupAddress = (host, port)
|
||||||
|
if clients.hasKey(addy):
|
||||||
|
return clients[addy]
|
||||||
|
result = newClient(addy)
|
||||||
|
clients[addy] = result
|
||||||
|
allClients.add(result)
|
||||||
|
|
||||||
|
proc loginZone(client: PClient; login: SdZoneLogin): bool =
|
||||||
|
if not client.auth:
|
||||||
|
for s in zoneSlots.items:
|
||||||
|
if s.name == login.name and s.key == login.key:
|
||||||
|
client.auth = true
|
||||||
|
client.kind = CServer
|
||||||
|
client.record = login.record
|
||||||
|
result = true
|
||||||
|
break
|
||||||
|
|
||||||
|
proc sendZoneList(client: PClient) =
|
||||||
|
echo(">> zonelist ", client, ' ', HZoneList)
|
||||||
|
client.send(HZonelist, zonelist)
|
||||||
|
proc forwardPrivate(rcv: PClient; sender: PClient; txt: string) =
|
||||||
|
var m = newScChat(CPriv, sender.alias, txt)
|
||||||
|
rcv.send(HChat, m)
|
||||||
|
proc sendChat(client: PClient; kind: ChatType; txt: string) =
|
||||||
|
echo(">> chat ", client)
|
||||||
|
var m = newScChat(kind, "", txt)
|
||||||
|
client.send(HChat, m)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var pubChatQueue = newIncomingBuffer()
|
||||||
|
proc queuePub(sender: string, msg: CsChat) =
|
||||||
|
var chat = newScChat(kind = CPub, fromPlayer = sender, text = msg.text)
|
||||||
|
pubChatQueue.write(HChat)
|
||||||
|
chat.pack(pubChatQueue)
|
||||||
|
|
||||||
|
handlers[HHello] = (proc(client: PClient; stream: PStream) =
|
||||||
|
var h = readCsHello(stream)
|
||||||
|
if h.i == 14:
|
||||||
|
var greet = newScHello("Well hello there")
|
||||||
|
client.send(HHello, greet))
|
||||||
|
handlers[HLogin] = proc(client: PClient; stream: PStream) =
|
||||||
|
var loginInfo = readCsLogin(stream)
|
||||||
|
echo("** login: alias = ", loginInfo.alias)
|
||||||
|
if alias2client.hasKey(loginInfo.alias):
|
||||||
|
client.sendError("Alias in use.")
|
||||||
|
return
|
||||||
|
if client.loginPlayer(loginInfo):
|
||||||
|
alias2client[client.alias] = client
|
||||||
|
client.sendMessage("Welcome "& client.alias)
|
||||||
|
var session = newScLogin(client.id, client.alias, client.session)
|
||||||
|
client.send HLogin, session
|
||||||
|
client.sendZonelist()
|
||||||
|
|
||||||
|
handlers[HZoneList] = proc(client: PClient; stream: PStream) =
|
||||||
|
var pinfo = readCsZoneList(stream)
|
||||||
|
echo("** zonelist req")
|
||||||
|
sendZoneList client
|
||||||
|
handlers[HChat] = proc(client: PClient; stream: PStream) =
|
||||||
|
var chat = readCsChat(stream)
|
||||||
|
if not client.auth:
|
||||||
|
client.sendError("You are not logged in.")
|
||||||
|
return
|
||||||
|
if chat.target != "": ##private
|
||||||
|
if alias2client.hasKey(chat.target):
|
||||||
|
alias2client[chat.target].forwardPrivate(client, chat.text)
|
||||||
|
else:
|
||||||
|
queuePub(client.alias, chat)
|
||||||
|
|
||||||
|
proc sendServMsg(client: PClient; msg: string) =
|
||||||
|
var m = newDsMsg(msg)
|
||||||
|
client.send HDsMsg, m
|
||||||
|
handlers[HZoneLogin] = proc(client: PClient; stream: PStream) =
|
||||||
|
var
|
||||||
|
login = readSdZoneLogin(stream)
|
||||||
|
if not client.loginZone(login):
|
||||||
|
client.sendServMsg "Invalid login"
|
||||||
|
else:
|
||||||
|
client.sendServMsg "Welcome to the servers"
|
||||||
|
echo "** Zone logged in: ", login
|
||||||
|
zones.add client
|
||||||
|
zonelist.zones.add client.record
|
||||||
|
|
||||||
|
|
||||||
|
handlers[HFileChallenge] = proc(client: PClient; stream: PStream) =
|
||||||
|
if client.auth:
|
||||||
|
if client.kind == CServer:
|
||||||
|
var chg = readScFileChallenge(stream)
|
||||||
|
|
||||||
|
proc handlePkt(s: PClient; stream: PStream) =
|
||||||
|
while not stream.atEnd:
|
||||||
|
var typ = readChar(stream)
|
||||||
|
if not handlers.hasKey(typ):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
handlers[typ](s, stream)
|
||||||
|
|
||||||
|
proc createServer(port: TPort) =
|
||||||
|
if not server.isNil:
|
||||||
|
server.close()
|
||||||
|
server = socket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, buffered = false)
|
||||||
|
server.bindAddr(port)
|
||||||
|
|
||||||
|
|
||||||
|
var clientIndex = 0
|
||||||
|
var incoming = newIncomingBuffer()
|
||||||
|
proc poll*(timeout: int = 250) =
|
||||||
|
if server.isNil: return
|
||||||
|
var
|
||||||
|
reads = @[server]
|
||||||
|
writes = @[server]
|
||||||
|
if select(reads, timeout) > 0:
|
||||||
|
var
|
||||||
|
addy = ""
|
||||||
|
port: TPort
|
||||||
|
incoming.data.setLen 512
|
||||||
|
let res = server.recvFromAsync(incoming.data, 512, addy, port, 0)
|
||||||
|
if not res:
|
||||||
|
echo("No recv")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
var client = findClient(addy, port.int16)
|
||||||
|
echo "<< ", res, " ", client, ": ", len(incoming.data), " ", repr(incoming.data)
|
||||||
|
handlePkt(client, incoming)
|
||||||
|
incoming.flush()
|
||||||
|
if selectWrite(writes, timeout) > 0:
|
||||||
|
let nclients = allClients.len
|
||||||
|
if nclients == 0:
|
||||||
|
return
|
||||||
|
clientIndex = (clientIndex + 1) mod nclients
|
||||||
|
var c = allClients[clientIndex]
|
||||||
|
if c.outputBuf.getPosition > 0:
|
||||||
|
let res = server.sendTo(c.addy.host, c.addy.port.TPort, c.outputBuf.data)
|
||||||
|
echo("Write ", c, " result: ", res, " data: ", repr(c.outputBuf.data))
|
||||||
|
c.outputBuf.flush()
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
import parseopt, matchers, strutils
|
||||||
|
var cfgFile = "dirserver_settings.json"
|
||||||
|
for kind, key, val in getOpt():
|
||||||
|
case kind
|
||||||
|
of cmdShortOption, cmdLongOption:
|
||||||
|
case key
|
||||||
|
of "f", "file":
|
||||||
|
if existsFile(val):
|
||||||
|
cfgFile = val
|
||||||
|
else:
|
||||||
|
echo("File does not exist: ", val)
|
||||||
|
else:
|
||||||
|
echo("Unknown option: ", key," ", val)
|
||||||
|
else:
|
||||||
|
echo("Unknown option: ", key, " ", val)
|
||||||
|
var jsonSettings = parseFile(cfgFile)
|
||||||
|
let port = TPort(jsonSettings["port"].num)
|
||||||
|
zonelist.network = jsonSettings["network"].str
|
||||||
|
for slot in jsonSettings["zones"].items:
|
||||||
|
zoneSlots.add((slot["name"].str, slot["key"].str))
|
||||||
|
|
||||||
|
createServer(port)
|
||||||
|
echo("Listening on port ", port, "...")
|
||||||
|
var pubChatTimer = cpuTime() #newClock()
|
||||||
|
const PubChatDelay = 1000/1000
|
||||||
|
while true:
|
||||||
|
poll(15)
|
||||||
|
## TODO sort this type of thing VV into a queue api
|
||||||
|
if cpuTime() - pubChatTimer > PubChatDelay: #.getElapsedTime.asMilliseconds > 100:
|
||||||
|
pubChatTimer -= pubChatDelay
|
||||||
|
if pubChatQueue.getPosition > 0:
|
||||||
|
var cn = 0
|
||||||
|
let sizePubChat = pubChatQueue.data.len
|
||||||
|
var sent = 0
|
||||||
|
filterIt2(allClients, it.auth == true and it.kind == CPlayer):
|
||||||
|
it.outputBuf.writeData(addr pubChatQueue.data[0], sizePubChat)
|
||||||
|
sent += 1
|
||||||
|
#for c in allClients:
|
||||||
|
# c.outputBuf.writeData(addr pubChatQueue.data[0], sizePubChat)
|
||||||
|
pubChatQueue.flush()
|
||||||
|
echo "pubChatQueue flushed to ", sent, "clients"
|
||||||
|
|
||||||
98
tests/manyloc/keineschweine/server/old_server_utils.nim
Normal file
98
tests/manyloc/keineschweine/server/old_server_utils.nim
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
import
|
||||||
|
streams, md5, sockets, unsigned,
|
||||||
|
sg_packets, zlib_helpers, idgen
|
||||||
|
type
|
||||||
|
TClientType* = enum
|
||||||
|
CServer = 0'i8, CPlayer, CUnknown
|
||||||
|
PClient* = ref TClient
|
||||||
|
TClient* = object of TObject
|
||||||
|
id*: int32
|
||||||
|
addy*: TupAddress
|
||||||
|
clientID*: uint16
|
||||||
|
auth*: bool
|
||||||
|
outputBuf*: PStringStream
|
||||||
|
case kind*: TClientType
|
||||||
|
of CPlayer:
|
||||||
|
alias*: string
|
||||||
|
session*: string
|
||||||
|
lastPing*: float
|
||||||
|
failedPings*: int
|
||||||
|
of CServer:
|
||||||
|
record*: ScZoneRecord
|
||||||
|
cfg*: TChecksumFile
|
||||||
|
of CUnknown: nil
|
||||||
|
TChecksumFile* = object
|
||||||
|
unpackedSize*: int
|
||||||
|
sum*: MD5Digest
|
||||||
|
compressed*: string
|
||||||
|
TupAddress* = tuple[host: string, port: int16]
|
||||||
|
PIDGen*[T: Ordinal] = ref TIDGen[T]
|
||||||
|
TIDGen[T: Ordinal] = object
|
||||||
|
max: T
|
||||||
|
freeIDs: seq[T]
|
||||||
|
var cliID = newIdGen[int32]()
|
||||||
|
|
||||||
|
proc sendMessage*(client: PClient; txt: string)
|
||||||
|
proc sendError*(client: PClient; txt: string)
|
||||||
|
proc `$`*(client: PClient): string
|
||||||
|
|
||||||
|
proc newIncomingBuffer*(size = 1024): PStringStream =
|
||||||
|
result = newStringStream("")
|
||||||
|
result.data.setLen size
|
||||||
|
result.data.setLen 0
|
||||||
|
result.flushImpl = proc(stream: PStream) =
|
||||||
|
stream.setPosition(0)
|
||||||
|
PStringStream(stream).data.setLen(0)
|
||||||
|
|
||||||
|
|
||||||
|
proc free*(c: PClient) =
|
||||||
|
echo "Client freed: ", c
|
||||||
|
cliID.del c.id
|
||||||
|
c.outputBuf.flush()
|
||||||
|
c.outputBuf = nil
|
||||||
|
proc newClient*(addy: TupAddress): PClient =
|
||||||
|
new(result, free)
|
||||||
|
result.addy = addy
|
||||||
|
result.outputBuf = newStringStream("")
|
||||||
|
result.outputBuf.flushImpl = proc(stream: PStream) =
|
||||||
|
stream.setPosition 0
|
||||||
|
PStringStream(stream).data.setLen 0
|
||||||
|
|
||||||
|
proc loginPlayer*(client: PClient; login: CsLogin): bool =
|
||||||
|
if client.auth:
|
||||||
|
client.sendError("You are already logged in.")
|
||||||
|
return
|
||||||
|
client.id = cliID.next()
|
||||||
|
client.auth = true
|
||||||
|
client.kind = CPlayer
|
||||||
|
client.alias = login.alias
|
||||||
|
client.session = getMD5(client.alias & $rand(10000))
|
||||||
|
result = true
|
||||||
|
|
||||||
|
proc `$`*(client: PClient): string =
|
||||||
|
if not client.auth: return $client.addy
|
||||||
|
case client.kind
|
||||||
|
of CPlayer: result = client.alias
|
||||||
|
of CServer: result = client.record.name
|
||||||
|
else: result = $client.addy
|
||||||
|
proc send*[T](client: PClient; pktType: char; pkt: var T) =
|
||||||
|
client.outputBuf.write(pktType)
|
||||||
|
pkt.pack(client.outputBuf)
|
||||||
|
|
||||||
|
proc sendMessage*(client: PClient; txt: string) =
|
||||||
|
var m = newScChat(CSystem, text = txt)
|
||||||
|
client.send HChat, m
|
||||||
|
proc sendError*(client: PClient; txt: string) =
|
||||||
|
var m = newScChat(CError, text = txt)
|
||||||
|
client.send HChat, m
|
||||||
|
|
||||||
|
proc checksumFile*(filename: string): TChecksumFile =
|
||||||
|
let fullText = readFile(filename)
|
||||||
|
result.unpackedSize = fullText.len
|
||||||
|
result.sum = toMD5(fullText)
|
||||||
|
result.compressed = compress(fullText)
|
||||||
|
proc checksumStr*(str: string): TChecksumFile =
|
||||||
|
result.unpackedSize = str.len
|
||||||
|
result.sum = toMD5(str)
|
||||||
|
result.compressed = compress(str)
|
||||||
|
|
||||||
254
tests/manyloc/keineschweine/server/old_sg_server.nim
Normal file
254
tests/manyloc/keineschweine/server/old_sg_server.nim
Normal file
|
|
@ -0,0 +1,254 @@
|
||||||
|
import
|
||||||
|
sockets, times, streams, streams_enh, tables, json, os, unsigned,
|
||||||
|
sg_packets, sg_assets, md5, server_utils, client_helpers
|
||||||
|
var
|
||||||
|
dirServer: PServer
|
||||||
|
thisZone = newScZoneRecord("local", "sup")
|
||||||
|
thisZoneSettings: PZoneSettings
|
||||||
|
dirServerConnected = false
|
||||||
|
## I was high.
|
||||||
|
clients = initTable[TupAddress, PClient](16)
|
||||||
|
alias2client = initTable[string, PClient](32)
|
||||||
|
allClients: seq[PClient] = @[]
|
||||||
|
zonePlayers: seq[PClient] = @[]
|
||||||
|
const
|
||||||
|
PubChatDelay = 100/1000 #100 ms
|
||||||
|
|
||||||
|
import hashes
|
||||||
|
proc hash*(x: uint16): THash {.inline.} =
|
||||||
|
result = int32(x)
|
||||||
|
|
||||||
|
proc findClient*(host: string; port: int16): PClient =
|
||||||
|
let addy: TupAddress = (host, port)
|
||||||
|
if clients.hasKey(addy):
|
||||||
|
return clients[addy]
|
||||||
|
result = newClient(addy)
|
||||||
|
clients[addy] = result
|
||||||
|
allClients.add(result)
|
||||||
|
|
||||||
|
|
||||||
|
proc sendZoneList(client: PClient) =
|
||||||
|
echo(">> zonelist ", client)
|
||||||
|
#client.send(HZonelist, zonelist)
|
||||||
|
|
||||||
|
proc forwardPrivate(rcv: PClient; sender: PClient; txt: string) =
|
||||||
|
var m = newScChat(CPriv, sender.alias, txt)
|
||||||
|
rcv.send(HChat, m)
|
||||||
|
proc sendChat(client: PClient; kind: ChatType; txt: string) =
|
||||||
|
echo(">> chat ", client)
|
||||||
|
var m = newScChat(kind, "", txt)
|
||||||
|
client.send(HChat, m)
|
||||||
|
|
||||||
|
var pubChatQueue = newStringStream("")
|
||||||
|
pubChatQueue.flushImpl = proc(stream: PStream) =
|
||||||
|
stream.setPosition(0)
|
||||||
|
PStringStream(stream).data.setLen(0)
|
||||||
|
proc queuePub(sender: string, msg: CsChat) =
|
||||||
|
var chat = newScChat(kind = CPub, fromPlayer = sender, text = msg.text)
|
||||||
|
pubChatQueue.write(HChat)
|
||||||
|
chat.pack(pubChatQueue)
|
||||||
|
|
||||||
|
handlers[HHello] = (proc(client: PClient; stream: PStream) =
|
||||||
|
var h = readCsHello(stream)
|
||||||
|
if h.i == 14:
|
||||||
|
var greet = newScHello("Well hello there")
|
||||||
|
client.send(HHello, greet))
|
||||||
|
handlers[HLogin] = proc(client: PClient; stream: PStream) =
|
||||||
|
var loginInfo = readCsLogin(stream)
|
||||||
|
echo("** login: alias = ", loginInfo.alias)
|
||||||
|
if not dirServerConnected and client.loginPlayer(loginInfo):
|
||||||
|
client.sendMessage("Welcome "& client.alias)
|
||||||
|
alias2client[client.alias] = client
|
||||||
|
client.sendZonelist()
|
||||||
|
handlers[HZoneList] = proc(client: PClient; stream: PStream) =
|
||||||
|
var pinfo = readCsZoneList(stream)
|
||||||
|
echo("** zonelist req")
|
||||||
|
handlers[HChat] = proc(client: PClient; stream: PStream) =
|
||||||
|
var chat = readCsChat(stream)
|
||||||
|
if not client.auth:
|
||||||
|
client.sendError("You are not logged in.")
|
||||||
|
return
|
||||||
|
if chat.target != "": ##private
|
||||||
|
if alias2client.hasKey(chat.target):
|
||||||
|
alias2client[chat.target].forwardPrivate(client, chat.text)
|
||||||
|
else:
|
||||||
|
queuePub(client.alias, chat)
|
||||||
|
handlers[HZoneQuery] = proc(client: PClient; stream: PStream) =
|
||||||
|
echo("Got zone query")
|
||||||
|
var q = readCsZoneQuery(stream)
|
||||||
|
var resp = newScZoneQuery(zonePlayers.len.uint16)
|
||||||
|
client.send(HZoneQuery, resp)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
handlers[HZoneJoinReq] = proc(client: PClient; stream: PStream) =
|
||||||
|
var req = readCsZoneJoinReq(stream)
|
||||||
|
echo "Join zone request from (",req.session.id,") ", req.session.alias
|
||||||
|
if client.auth and client.kind == CPlayer:
|
||||||
|
echo "Client is authenticated, verifying filez"
|
||||||
|
client.startVerifyingFiles()
|
||||||
|
elif dirServerConnected:
|
||||||
|
echo "Dirserver is connected, verifying client"
|
||||||
|
dirServer.send HVerifyClient, req.session
|
||||||
|
else:
|
||||||
|
echo "Dirserver is disconnected =("
|
||||||
|
client.startVerifyingFiles()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc handlePkt(s: PClient; stream: PStream) =
|
||||||
|
while not stream.atEnd:
|
||||||
|
var typ = readChar(stream)
|
||||||
|
if not handlers.hasKey(typ):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
handlers[typ](s, stream)
|
||||||
|
|
||||||
|
proc createServer(port: TPort) =
|
||||||
|
if not server.isNil:
|
||||||
|
server.close()
|
||||||
|
server = socket(typ = SOCK_DGRAM, protocol = IPPROTO_UDP, buffered = false)
|
||||||
|
server.bindAddr(port)
|
||||||
|
|
||||||
|
var clientIndex = 0
|
||||||
|
var incoming = newIncomingBuffer()
|
||||||
|
proc poll*(timeout: int = 250) =
|
||||||
|
if server.isNil: return
|
||||||
|
var
|
||||||
|
reads = @[server]
|
||||||
|
writes = @[server]
|
||||||
|
if select(reads, timeout) > 0:
|
||||||
|
var
|
||||||
|
addy = ""
|
||||||
|
port: TPort
|
||||||
|
let res = server.recvFromAsync(incoming.data, 512, addy, port, 0)
|
||||||
|
if not res:
|
||||||
|
echo("No recv")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
var client = findClient(addy, port.int16)
|
||||||
|
#echo("<< ", res, " ", client.alias, ": ", len(line.data), " ", repr(line.data))
|
||||||
|
handlePkt(client, incoming)
|
||||||
|
incoming.flush()
|
||||||
|
if selectWrite(writes, timeout) > 0:
|
||||||
|
let nclients = allClients.len
|
||||||
|
if nclients == 0:
|
||||||
|
return
|
||||||
|
clientIndex = (clientIndex + 1) mod nclients
|
||||||
|
var c = allClients[clientIndex]
|
||||||
|
if c.outputBuf.getPosition > 0:
|
||||||
|
let res = server.sendTo(c.addy.host, c.addy.port.TPort, c.outputBuf.data)
|
||||||
|
echo("Write ", c, " result: ", res, " data: ", c.outputBuf.data)
|
||||||
|
c.outputBuf.flush()
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
import parseopt, matchers, strutils
|
||||||
|
var zoneCfgFile = "./server_settings.json"
|
||||||
|
for kind, key, val in getOpt():
|
||||||
|
case kind
|
||||||
|
of cmdShortOption, cmdLongOption:
|
||||||
|
case key
|
||||||
|
of "f", "file":
|
||||||
|
if existsFile(val):
|
||||||
|
zoneCfgFile = val
|
||||||
|
else:
|
||||||
|
echo("File does not exist: ", val)
|
||||||
|
else:
|
||||||
|
echo("Unknown option: ", key," ", val)
|
||||||
|
else:
|
||||||
|
echo("Unknown option: ", key, " ", val)
|
||||||
|
var jsonSettings = parseFile(zoneCfgFile)
|
||||||
|
let
|
||||||
|
host = jsonSettings["host"].str
|
||||||
|
port = TPort(jsonSettings["port"].num)
|
||||||
|
zoneFile = jsonSettings["settings"].str
|
||||||
|
dirServerInfo = jsonSettings["dirserver"]
|
||||||
|
|
||||||
|
var path = getAppDir()/../"data"/zoneFile
|
||||||
|
if not existsFile(path):
|
||||||
|
echo("Zone settings file does not exist: ../data/", zoneFile)
|
||||||
|
echo(path)
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
## Test file
|
||||||
|
block:
|
||||||
|
var
|
||||||
|
TestFile: FileChallengePair
|
||||||
|
contents = repeatStr(2, "abcdefghijklmnopqrstuvwxyz")
|
||||||
|
testFile.challenge = newScFileChallenge("foobar.test", FZoneCfg, contents.len.int32)
|
||||||
|
testFile.file = checksumStr(contents)
|
||||||
|
myAssets.add testFile
|
||||||
|
|
||||||
|
setCurrentDir getAppDir().parentDir()
|
||||||
|
block:
|
||||||
|
let zonesettings = readFile(path)
|
||||||
|
var
|
||||||
|
errors: seq[string] = @[]
|
||||||
|
if not loadSettings(zoneSettings, errors):
|
||||||
|
echo("You have errors in your zone settings:")
|
||||||
|
for e in errors: echo("**", e)
|
||||||
|
quit(1)
|
||||||
|
errors.setLen 0
|
||||||
|
|
||||||
|
var pair: FileChallengePair
|
||||||
|
pair.challenge.file = zoneFile
|
||||||
|
pair.challenge.assetType = FZoneCfg
|
||||||
|
pair.challenge.fullLen = zoneSettings.len.int32
|
||||||
|
pair.file = checksumStr(zoneSettings)
|
||||||
|
myAssets.add pair
|
||||||
|
|
||||||
|
allAssets:
|
||||||
|
if not load(asset):
|
||||||
|
echo "Invalid or missing file ", file
|
||||||
|
else:
|
||||||
|
var pair: FileChallengePair
|
||||||
|
pair.challenge.file = file
|
||||||
|
pair.challenge.assetType = assetType
|
||||||
|
pair.challenge.fullLen = getFileSize(
|
||||||
|
expandPath(assetType, file)).int32
|
||||||
|
pair.file = asset.contents
|
||||||
|
myAssets.add pair
|
||||||
|
|
||||||
|
echo "Zone has ", myAssets.len, " associated assets"
|
||||||
|
|
||||||
|
|
||||||
|
dirServer = newServerConnection(dirServerInfo[0].str, dirServerInfo[1].num.TPort)
|
||||||
|
dirServer.handlers[HDsMsg] = proc(serv: PServer; stream: PStream) =
|
||||||
|
var m = readDsMsg(stream)
|
||||||
|
echo("DirServer> ", m.msg)
|
||||||
|
dirServer.handlers[HZoneLogin] = proc(serv: PServer; stream: PStream) =
|
||||||
|
let loggedIn = readDsZoneLogin(stream).status
|
||||||
|
if loggedIn:
|
||||||
|
dirServerConnected = true
|
||||||
|
dirServer.writePkt HZoneLogin, login
|
||||||
|
|
||||||
|
thisZone.name = jsonSettings["name"].str
|
||||||
|
thisZone.desc = jsonSettings["desc"].str
|
||||||
|
thisZone.ip = "localhost"
|
||||||
|
thisZone.port = port
|
||||||
|
var login = newSdZoneLogin(
|
||||||
|
dirServerInfo[2].str, dirServerInfo[3].str,
|
||||||
|
thisZone)
|
||||||
|
#echo "MY LOGIN: ", $login
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
createServer(port)
|
||||||
|
echo("Listening on port ", port, "...")
|
||||||
|
var pubChatTimer = cpuTime()#newClock()
|
||||||
|
while true:
|
||||||
|
discard dirServer.pollServer(15)
|
||||||
|
poll(15)
|
||||||
|
## TODO sort this type of thing VV into a queue api
|
||||||
|
#let now = cpuTime()
|
||||||
|
if cpuTime() - pubChatTimer > PubChatDelay: #.getElapsedTime.asMilliseconds > 100:
|
||||||
|
pubChatTimer -= pubChatDelay #.restart()
|
||||||
|
if pubChatQueue.getPosition > 0:
|
||||||
|
var cn = 0
|
||||||
|
let sizePubChat = pubChatQueue.data.len
|
||||||
|
for c in allClients:
|
||||||
|
c.outputBuf.writeData(addr pubChatQueue.data[0], sizePubChat)
|
||||||
|
pubChatQueue.flush()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
267
tests/manyloc/keineschweine/server/sg_lobby.nim
Normal file
267
tests/manyloc/keineschweine/server/sg_lobby.nim
Normal file
|
|
@ -0,0 +1,267 @@
|
||||||
|
|
||||||
|
import
|
||||||
|
sockets, streams, tables, times, math, strutils, json, os, md5,
|
||||||
|
sfml, sfml_vector, sfml_colors,
|
||||||
|
streams_enh, input_helpers, zlib_helpers, client_helpers, sg_packets, sg_assets, sg_gui
|
||||||
|
type
|
||||||
|
TClientSettings = object
|
||||||
|
resolution*: TVideoMode
|
||||||
|
offlineFile: string
|
||||||
|
dirserver: tuple[host: string, port: TPort]
|
||||||
|
website*: string
|
||||||
|
var
|
||||||
|
clientSettings: TClientSettings
|
||||||
|
gui = newGuiContainer()
|
||||||
|
zonelist = newGuiContainer()
|
||||||
|
u_alias, u_passwd: PTextEntry
|
||||||
|
activeInput = 0
|
||||||
|
aliasText, passwdText: PText
|
||||||
|
fpsTimer: PButton
|
||||||
|
loginBtn: PButton
|
||||||
|
playBtn: PButton
|
||||||
|
keyClient = newKeyClient("lobby")
|
||||||
|
showZonelist = false
|
||||||
|
chatInput*: PTextEntry
|
||||||
|
messageArea*: PMessageArea
|
||||||
|
mySession*: ScLogin
|
||||||
|
var
|
||||||
|
dirServer: PServer
|
||||||
|
zone*: PServer
|
||||||
|
activeServer: PServer
|
||||||
|
bConnected = false
|
||||||
|
outgoing = newStringStream("")
|
||||||
|
downloadProgress: PButton
|
||||||
|
connectionButtons: seq[PButton] #buttons that depend on connection to function
|
||||||
|
|
||||||
|
template dispmessage(m: expr): stmt =
|
||||||
|
messageArea.add(m)
|
||||||
|
proc connectZone(host: string; port: TPort)
|
||||||
|
proc connectToDirserv()
|
||||||
|
|
||||||
|
proc writePkt[T](pid: PacketID; p: var T) =
|
||||||
|
if activeServer.isNil: return
|
||||||
|
activeServer.writePkt pid, p
|
||||||
|
|
||||||
|
proc setConnected(state: bool) =
|
||||||
|
if state:
|
||||||
|
bConnected = true
|
||||||
|
for b in connectionButtons: enable(b)
|
||||||
|
else:
|
||||||
|
bConnected = false
|
||||||
|
for b in connectionButtons: disable(b)
|
||||||
|
|
||||||
|
proc setActiveZone(ind: int; zone: ScZoneRecord) =
|
||||||
|
#hilight it or something
|
||||||
|
dispmessage("Selected " & zone.name)
|
||||||
|
connectZone(zone.ip, zone.port)
|
||||||
|
playBtn.enable()
|
||||||
|
|
||||||
|
proc handleChat(serv: PServer; s: PStream) =
|
||||||
|
var msg = readScChat(s)
|
||||||
|
messageArea.add(msg)
|
||||||
|
|
||||||
|
proc connectToDirserv() =
|
||||||
|
if dirServer.isNil:
|
||||||
|
dirServer = newServerConnection(clientSettings.dirserver.host, clientSettings.dirserver.port)
|
||||||
|
dirServer.handlers[HHello] = proc(serv: PServer; s: PStream) =
|
||||||
|
let msg = readScHello(s)
|
||||||
|
dispMessage(msg.resp)
|
||||||
|
setConnected(true)
|
||||||
|
dirServer.handlers[HLogin] = proc(serv: PServer; s: PStream) =
|
||||||
|
mySession = readScLogin(s)
|
||||||
|
##do something here
|
||||||
|
dirServer.handlers[HZonelist] = proc(serv: PServer; s: PStream) =
|
||||||
|
var
|
||||||
|
info = readScZonelist(s)
|
||||||
|
zones = info.zones
|
||||||
|
if zones.len > 0:
|
||||||
|
zonelist.clearButtons()
|
||||||
|
var pos = vec2f(0.0, 0.0)
|
||||||
|
zonelist.newButton(
|
||||||
|
text = "Zonelist - "& info.network,
|
||||||
|
position = pos,
|
||||||
|
onClick = proc(b: PButton) =
|
||||||
|
dispmessage("Click on header"))
|
||||||
|
pos.y += 20
|
||||||
|
for i in 0..zones.len - 1:
|
||||||
|
var z = zones[i]
|
||||||
|
zonelist.newButton(
|
||||||
|
text = z.name, position = pos,
|
||||||
|
onClick = proc(b: PButton) =
|
||||||
|
setActiveZone(i, z))
|
||||||
|
pos.y += 20
|
||||||
|
showZonelist = true
|
||||||
|
dirServer.handlers[HPoing] = proc(serv: PServer; s: PStream) =
|
||||||
|
var ping = readPoing(s)
|
||||||
|
dispmessage("Ping: "& $ping.time)
|
||||||
|
ping.time = epochTime().float32
|
||||||
|
serv.writePkt HPoing, ping
|
||||||
|
dirServer.handlers[HChat] = handleChat
|
||||||
|
dirServer.handlers[HFileChallenge] = handleFileChallenge
|
||||||
|
var hello = newCsHello()
|
||||||
|
dirServer.writePkt HHello, hello
|
||||||
|
activeServer = dirServer
|
||||||
|
|
||||||
|
|
||||||
|
proc zoneListReq() =
|
||||||
|
var pkt = newCsZonelist("sup")
|
||||||
|
writePkt HZonelist, pkt
|
||||||
|
|
||||||
|
##key handlers
|
||||||
|
keyClient.registerHandler(MouseMiddle, down, proc() =
|
||||||
|
gui.setPosition(getMousePos()))
|
||||||
|
|
||||||
|
keyClient.registerHandler(KeyO, down, proc() =
|
||||||
|
if keyPressed(KeyRShift): echo(repr(outgoing)))
|
||||||
|
keyClient.registerHandler(KeyTab, down, proc() =
|
||||||
|
activeInput = (activeInput + 1) mod 2) #does this work?
|
||||||
|
keyClient.registerHandler(MouseLeft, down, proc() =
|
||||||
|
let p = getMousePos()
|
||||||
|
gui.click(p)
|
||||||
|
if showZonelist: zonelist.click(p))
|
||||||
|
var mptext = newText("", guiFont, 16)
|
||||||
|
keyClient.registerHandler(MouseRight, down, proc() =
|
||||||
|
let p = getMousePos()
|
||||||
|
mptext.setPosition(p)
|
||||||
|
mptext.setString("($1,$2)"%[$p.x.int,$p.y.int]))
|
||||||
|
|
||||||
|
|
||||||
|
proc connectZone(host: string, port: TPort) =
|
||||||
|
echo "Connecting to zone at ", host, ':', port
|
||||||
|
if zone.isNil:
|
||||||
|
zone = newServerConnection(host, port)
|
||||||
|
zone.handlers[HFileChallenge] = handleFileChallenge
|
||||||
|
zone.handlers[HChallengeResult] = handleFileChallengeResult
|
||||||
|
zone.handlers[HFileTransfer] = handleFileTransfer
|
||||||
|
zone.handlers[HChat] = handleChat
|
||||||
|
else:
|
||||||
|
zone.sock.connect(host, port)
|
||||||
|
var hello = newCsHello()
|
||||||
|
zone.writePkt HHello, hello
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc lobbyReady*() =
|
||||||
|
keyClient.setActive()
|
||||||
|
gui.setActive(u_alias)
|
||||||
|
|
||||||
|
proc tryConnect*(b: PButton) =
|
||||||
|
connectToDirserv()
|
||||||
|
proc tryLogin*(b: PButton) =
|
||||||
|
var login = newCsLogin(
|
||||||
|
alias = u_alias.getText(),
|
||||||
|
passwd = u_passwd.getText())
|
||||||
|
writePkt HLogin, login
|
||||||
|
proc tryTransition*(b: PButton) =
|
||||||
|
##check if we're logged in
|
||||||
|
#<implementation censored by the church>
|
||||||
|
#var joinReq = newCsJ
|
||||||
|
zone.writePkt HZoneJoinReq, mySession
|
||||||
|
#var errors: seq[string] = @[]
|
||||||
|
#if loadSettings("", errors):
|
||||||
|
# transition()
|
||||||
|
#else:
|
||||||
|
# for e in errors: dispmessage(e)
|
||||||
|
proc playOffline*(b: PButton) =
|
||||||
|
var errors: seq[string] = @[]
|
||||||
|
if loadSettingsFromFile(clientSettings.offlineFile, errors):
|
||||||
|
transition()
|
||||||
|
else:
|
||||||
|
dispmessage("Errors reading the file ("& clientSettings.offlineFile &"):")
|
||||||
|
for e in errors: dispmessage(e)
|
||||||
|
|
||||||
|
proc getClientSettings*(): TClientSettings =
|
||||||
|
result = clientSettings
|
||||||
|
|
||||||
|
proc lobbyInit*() =
|
||||||
|
var s = json.parseFile("./client_settings.json")
|
||||||
|
clientSettings.offlineFile = "data/"
|
||||||
|
clientSettings.offlineFile.add s["default-file"].str
|
||||||
|
let dirserv = s["directory-server"]
|
||||||
|
clientSettings.dirserver.host = dirserv["host"].str
|
||||||
|
clientSettings.dirserver.port = dirserv["port"].num.TPort
|
||||||
|
clientSettings.resolution.width = s["resolution"][0].num.cint
|
||||||
|
clientSettings.resolution.height= s["resolution"][1].num.cint
|
||||||
|
clientSettings.resolution.bitsPerPixel = s["resolution"][2].num.cint
|
||||||
|
clientSettings.website = s["website"].str
|
||||||
|
zonelist.setPosition(vec2f(200.0, 100.0))
|
||||||
|
connectionButtons = @[]
|
||||||
|
|
||||||
|
downloadProgress = gui.newButton(
|
||||||
|
text = "", position = vec2f(10, 130), onClick = nil)
|
||||||
|
downloadProgress.bg.setFillColor(color(34, 139, 34))
|
||||||
|
downloadProgress.bg.setSize(vec2f(0, 0))
|
||||||
|
|
||||||
|
var pos = vec2f(10, 10)
|
||||||
|
u_alias = gui.newTextEntry(
|
||||||
|
if s.existsKey("alias"): s["alias"].str else: "alias",
|
||||||
|
pos)
|
||||||
|
pos.y += 20
|
||||||
|
u_passwd = gui.newTextEntry("buzz", pos)
|
||||||
|
pos.y += 20
|
||||||
|
connectionButtons.add(gui.newButton(
|
||||||
|
text = "Login",
|
||||||
|
position = pos,
|
||||||
|
onClick = tryLogin,
|
||||||
|
startEnabled = false))
|
||||||
|
pos.y += 20
|
||||||
|
fpsText.setPosition(pos)
|
||||||
|
|
||||||
|
playBtn = gui.newButton(
|
||||||
|
text = "Play",
|
||||||
|
position = vec2f(680.0, 8.0),
|
||||||
|
onClick = tryTransition,
|
||||||
|
startEnabled = false)
|
||||||
|
gui.newButton(
|
||||||
|
text = "Play Offline",
|
||||||
|
position = vec2f(680.0, 28.0),
|
||||||
|
onClick = playOffline)
|
||||||
|
fpsTimer = gui.newButton(
|
||||||
|
text = "FPS: ",
|
||||||
|
position = vec2f(10.0, 70.0),
|
||||||
|
onClick = proc(b: PButton) = nil)
|
||||||
|
gui.newButton(
|
||||||
|
text = "Connect",
|
||||||
|
position = vec2f(10.0, 90.0),
|
||||||
|
onClick = tryConnect)
|
||||||
|
connectionButtons.add(gui.newButton(
|
||||||
|
text = "Test Chat",
|
||||||
|
position = vec2f(10.0, 110.0),
|
||||||
|
onClick = (proc(b: PButton) =
|
||||||
|
var pkt = newCsChat(text = "ohai")
|
||||||
|
writePkt HChat, pkt),
|
||||||
|
startEnabled = false))
|
||||||
|
chatInput = gui.newTextEntry("...", vec2f(10.0, 575.0), proc() =
|
||||||
|
sendChat dirServer, chatInput.getText()
|
||||||
|
chatInput.clearText())
|
||||||
|
messageArea = gui.newMessageArea(vec2f(10.0, 575.0 - 20.0))
|
||||||
|
messageArea.sizeVisible = 25
|
||||||
|
gui.newButton(text = "Scrollback + 1", position = vec2f(185, 10), onClick = proc(b: PButton) =
|
||||||
|
messageArea.scrollBack += 1
|
||||||
|
update(messageArea))
|
||||||
|
gui.newButton(text = "Scrollback - 1", position = vec2f(185+160, 10), onClick = proc(b: PButton) =
|
||||||
|
messageArea.scrollBack -= 1
|
||||||
|
update(messageArea))
|
||||||
|
gui.newButton(text = "Flood msg area", position = vec2f(185, 30), onClick = proc(b: PButton) =
|
||||||
|
for i in 0.. <30:
|
||||||
|
dispMessage($i))
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
proc lobbyUpdate*(dt: float) =
|
||||||
|
#let res = disp.poll()
|
||||||
|
gui.update(dt)
|
||||||
|
i = (i + 1) mod 60
|
||||||
|
if i == 0:
|
||||||
|
fpsTimer.setString("FPS: "& $round(1.0/dt))
|
||||||
|
if not pollServer(dirServer, 5) and bConnected:
|
||||||
|
setConnected(false)
|
||||||
|
echo("Lost connection")
|
||||||
|
discard pollServer(zone, 5)
|
||||||
|
|
||||||
|
proc lobbyDraw*(window: PRenderWindow) =
|
||||||
|
window.clear(Black)
|
||||||
|
window.draw messageArea
|
||||||
|
window.draw mptext
|
||||||
|
window.draw gui
|
||||||
|
if showZonelist: window.draw zonelist
|
||||||
|
window.display()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue