mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-06 20:04:18 +00:00
added manyloc test suite; --path now relative to project dir if not absolute
This commit is contained in:
@@ -196,8 +196,10 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
|
||||
of "patterns": result = contains(gOptions, optPatterns)
|
||||
else: InvalidCmdLineOption(passCmd1, switch, info)
|
||||
|
||||
proc processPath(path: string): string =
|
||||
result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath,
|
||||
proc processPath(path: string, notRelativeToProj = false): string =
|
||||
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()),
|
||||
"projectname", options.gProjectName,
|
||||
"projectpath", options.gProjectPath])
|
||||
@@ -229,7 +231,7 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
|
||||
of "babelpath":
|
||||
if pass in {passCmd2, passPP}:
|
||||
expectArg(switch, arg, pass, info)
|
||||
let path = processPath(arg)
|
||||
let path = processPath(arg, notRelativeToProj=true)
|
||||
babelpath(path, info)
|
||||
of "excludepath":
|
||||
expectArg(switch, arg, pass, info)
|
||||
@@ -451,9 +453,9 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
|
||||
of "genscript":
|
||||
expectNoArg(switch, arg, pass, info)
|
||||
incl(gGlobalOptions, optGenScript)
|
||||
of "lib":
|
||||
of "lib":
|
||||
expectArg(switch, arg, pass, info)
|
||||
libpath = processPath(arg)
|
||||
libpath = processPath(arg, notRelativeToProj=true)
|
||||
of "putenv":
|
||||
expectArg(switch, arg, pass, info)
|
||||
splitSwitch(arg, key, val, pass, info)
|
||||
|
||||
@@ -36,3 +36,11 @@ for i in 1 .. count-1:
|
||||
echo(surface.WriteToPNG(outFile))
|
||||
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()
|
||||
Reference in New Issue
Block a user