Skip to content

Commit 2b18473

Browse files
committed
Timer, roomname
* Add `timer` script command, with `%t` variable * Add `roomname` global variable * Fix bug in emoji parsing * Fix allow leading whitespace with `echo` command * Fix trim leading whitespace with triggers
1 parent 30a08f7 commit 2b18473

File tree

10 files changed

+166
-6
lines changed

10 files changed

+166
-6
lines changed

app/src/Outlander.xcodeproj/project.pbxproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@
131131
7BBBC51C276DD0F800D1CF3E /* AtomicQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBC51A276DD0F800D1CF3E /* AtomicQueue.swift */; };
132132
7BBBC51E2772C4E800D1CF3E /* EditCommandHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBC51D2772C4E800D1CF3E /* EditCommandHandler.swift */; };
133133
7BBBC51F2772C4E800D1CF3E /* EditCommandHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBC51D2772C4E800D1CF3E /* EditCommandHandler.swift */; };
134+
7BBBC5212776912700D1CF3E /* DateFormats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBC5202776912700D1CF3E /* DateFormats.swift */; };
135+
7BBBC5222776912700D1CF3E /* DateFormats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBC5202776912700D1CF3E /* DateFormats.swift */; };
134136
7BC74D1B2755D37300B6DF62 /* LinkCommandHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC74D1A2755D37300B6DF62 /* LinkCommandHandler.swift */; };
135137
7BC74D1C2755D37300B6DF62 /* LinkCommandHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC74D1A2755D37300B6DF62 /* LinkCommandHandler.swift */; };
136138
7BC74D1D27575B4300B6DF62 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9BCF2ED22E177E100C7F7F0 /* AppDelegate.swift */; };
@@ -371,6 +373,7 @@
371373
7BBBC517276DA3E400D1CF3E /* LogBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogBuilder.swift; sourceTree = "<group>"; };
372374
7BBBC51A276DD0F800D1CF3E /* AtomicQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtomicQueue.swift; sourceTree = "<group>"; };
373375
7BBBC51D2772C4E800D1CF3E /* EditCommandHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCommandHandler.swift; sourceTree = "<group>"; };
376+
7BBBC5202776912700D1CF3E /* DateFormats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormats.swift; sourceTree = "<group>"; };
374377
7BC74D1A2755D37300B6DF62 /* LinkCommandHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkCommandHandler.swift; sourceTree = "<group>"; };
375378
7BCBBD0C273F499D00219CA0 /* VariableReplacerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariableReplacerTests.swift; sourceTree = "<group>"; };
376379
7BCBBD0E2743ABEA00219CA0 /* VariableTokenizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VariableTokenizer.swift; sourceTree = "<group>"; };
@@ -635,6 +638,7 @@
635638
7B917E5B27641E5300887F95 /* SimplePing.m */,
636639
7B917E5E27641E9700887F95 /* SimplePingAdapter.swift */,
637640
7B917E6127641EC200887F95 /* PlainPing.swift */,
641+
7BBBC5202776912700D1CF3E /* DateFormats.swift */,
638642
);
639643
path = Infrastructure;
640644
sourceTree = "<group>";
@@ -1026,6 +1030,7 @@
10261030
7BCBBD152746D1D800219CA0 /* ExpressionEvaluator.swift in Sources */,
10271031
7B27C5AB274ADE1A0095B9BF /* ExpressionTokenizer.swift in Sources */,
10281032
F9CA2063246669D800A92736 /* FileSystem.swift in Sources */,
1033+
7BBBC5212776912700D1CF3E /* DateFormats.swift in Sources */,
10291034
F969A0EB239C812900408CE5 /* WindowViewController.swift in Sources */,
10301035
F98B491D2470AF17000E2D92 /* GameContext.swift in Sources */,
10311036
F98B4909246F9668000E2D92 /* HighlightLoader.swift in Sources */,
@@ -1154,6 +1159,7 @@
11541159
F9CA205E24664A4500A92736 /* VarCommandHandlerTests.swift in Sources */,
11551160
7B917E5A27641E3600887F95 /* SimplePing.h in Sources */,
11561161
F9CA204D245CAA9700A92736 /* EventBus.swift in Sources */,
1162+
7BBBC5222776912700D1CF3E /* DateFormats.swift in Sources */,
11571163
7BCBBD132746C03E00219CA0 /* FunctionEvaluator.swift in Sources */,
11581164
7BC74D1D27575B4300B6DF62 /* AppDelegate.swift in Sources */,
11591165
7B228B0D2734F8E80052610B /* ExpTracker.swift in Sources */,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// DateFormats.swift
3+
// Outlander
4+
//
5+
// Created by Joe McBride on 12/24/21.
6+
// Copyright © 2021 Joe McBride. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
struct DateFormats {
12+
internal static var sharedFormatter = DateFormatter()
13+
14+
internal static let builtInAutoFormat: [String] = [
15+
"yyyy-MM-dd HH:mm",
16+
"yyyy-MM-dd HH:mm:ss",
17+
"yyyy-MM-dd hh:mm:ss a",
18+
"yyyy/MM/dd HH:mm",
19+
"yyyy/MM/dd HH:mm:ss",
20+
"yyyy/MM/dd HH:mm:ss a",
21+
"MM-dd-yyyy HH:mm",
22+
"MM-dd-yyyy HH:mm:ss",
23+
"MM-dd-yyyy HH:mm:ss a",
24+
"MM/dd/yyyy HH:mm",
25+
"MM/dd/yyyy HH:mm:ss",
26+
"MM/dd/yyyy HH:mm:ss a",
27+
]
28+
29+
public static func resetAutoFormats() {
30+
autoFormats = DateFormats.builtInAutoFormat
31+
}
32+
33+
public static var autoFormats: [String] = DateFormats.builtInAutoFormat
34+
35+
public static func parse(_ string: String, format: String? = nil) -> Date? {
36+
let formats = (format != nil ? [format!] : DateFormats.autoFormats)
37+
return DateFormats.parse(string, formats: formats)
38+
}
39+
40+
public static func parse(_ string: String, formats: [String]) -> Date? {
41+
let formatter = sharedFormatter
42+
43+
var parsedDate: Date?
44+
for format in formats {
45+
formatter.dateFormat = format
46+
if let date = formatter.date(from: string) {
47+
parsedDate = date
48+
break
49+
}
50+
}
51+
return parsedDate
52+
}
53+
}

app/src/Outlander/Infrastructure/GameContext.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class GameContext {
2626
var mapZone: MapZone? {
2727
didSet {
2828
globalVars["zoneid"] = mapZone?.id ?? ""
29+
globalVars["zonename"] = mapZone?.name ?? ""
2930
}
3031
}
3132

app/src/Outlander/Infrastructure/StringExtensions.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ extension String {
217217
}
218218
} else {
219219
// append the data as is
220+
if emojiStr.count > 0 {
221+
newData.append(emojiStr.data(using: .utf8)!)
222+
emojiStr = ""
223+
}
220224
newData.append(str.data(using: .utf8)!)
221225
}
222226
}

app/src/Outlander/Infrastructure/Variables.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,45 @@ class GlobalVariables: Variables {
9494
}
9595
}
9696

97+
class ScriptVariables: Variables {
98+
private var clock: IClock
99+
private var settings: ApplicationSettings
100+
private var timerStartTime: Date?
101+
private var timerEndTime: Date?
102+
103+
init(events: Events, settings: ApplicationSettings, clock: IClock = Clock()) {
104+
self.clock = clock
105+
self.settings = settings
106+
super.init(eventKey: "", events: events)
107+
}
108+
109+
func startTimer(_ at: Date? = nil) {
110+
timerStartTime = at ?? clock.now
111+
timerEndTime = nil
112+
}
113+
114+
func stopTimer() {
115+
timerEndTime = clock.now
116+
}
117+
118+
func clearTimer() {
119+
timerStartTime = nil
120+
timerEndTime = nil
121+
}
122+
123+
override func addDynamics() {
124+
addDynamic(key: "t", value: .dynamic {
125+
guard let start = self.timerStartTime else {
126+
return nil
127+
}
128+
129+
let end = self.timerEndTime ?? self.clock.now
130+
let diff = end.timeIntervalSince(start)
131+
return "\(diff.formattedNumber)"
132+
})
133+
}
134+
}
135+
97136
class Variables {
98137
private let lock = NSRecursiveLock()
99138
private var vars: [String: DynamicValue] = [:]

app/src/Outlander/Plugins/AutoMapperPlugin.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ class AutoMapperPlugin: OPlugin {
9595
let exits = swapped.nonCardinalExists().map { $0.move }.joined(separator: ", ")
9696
if assignRoom {
9797
host?.set(variable: "roomid", value: swapped.id)
98+
host?.set(variable: "roomname", value: swapped.name)
9899
}
99100

100101
guard exits.count > 0 else {
@@ -147,6 +148,7 @@ extension GameContext {
147148
if let zone = mapZone {
148149
if let currentRoom = zone.findRoomFuzyFrom(previousRoomId: nil, name: title, description: description, exits: exits, ignoreTransfers: true) {
149150
globalVars["roomid"] = currentRoom.id
151+
globalVars["roomname"] = currentRoom.name
150152
} else {
151153
_ = findRoomInZones(name: title, description: description)
152154
}

app/src/Outlander/Scripting/Script.swift

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ class Script {
256256
tokenHandlers["save"] = handleSave
257257
tokenHandlers["send"] = handleSend
258258
tokenHandlers["shift"] = handleShift
259+
tokenHandlers["timer"] = handleTimer
259260
tokenHandlers["unvar"] = handleUnVar
260261
tokenHandlers["variable"] = handleVariable
261262
tokenHandlers["waiteval"] = handleWaitEval
@@ -374,7 +375,7 @@ class Script {
374375

375376
stackTrace.push(line)
376377

377-
log.info("passing \(line.lineNumber) - \(line.originalText)")
378+
// log.info("passing \(line.lineNumber) - \(line.originalText)")
378379

379380
let result = handleLine(line)
380381

@@ -404,7 +405,6 @@ class Script {
404405
}
405406

406407
func nextAfterRoundtime() {
407-
print("next after roundtime")
408408
_nextAfterRoundtime()
409409
}
410410

@@ -1751,6 +1751,34 @@ class Script {
17511751
return .next
17521752
}
17531753

1754+
func handleTimer(_ line: ScriptLine, _ token: ScriptTokenValue) -> ScriptExecuteResult {
1755+
guard case let .timer(command, maybeDate) = token else {
1756+
return .next
1757+
}
1758+
1759+
let resolvedCommand = context.replaceVars(command)
1760+
let resolvedMaybeDate = context.replaceVars(maybeDate)
1761+
1762+
notify("timer \(resolvedCommand) \(resolvedMaybeDate)", debug: ScriptLogLevel.vars, scriptLine: line.lineNumber, fileName: line.fileName)
1763+
1764+
switch resolvedCommand.lowercased() {
1765+
case "start":
1766+
context.variables.startTimer()
1767+
case "stop":
1768+
context.variables.stopTimer()
1769+
case "clear":
1770+
context.variables.clearTimer()
1771+
case "setstart":
1772+
let date = DateFormats.parse(resolvedMaybeDate)
1773+
print("parsed date: \(String(describing: date))")
1774+
context.variables.startTimer(date)
1775+
default:
1776+
sendText("unknown timer command '\(resolvedCommand)'", preset: "scripterror", scriptLine: line.lineNumber, fileName: line.fileName)
1777+
}
1778+
1779+
return .next
1780+
}
1781+
17541782
func handleUnVar(_ line: ScriptLine, _ token: ScriptTokenValue) -> ScriptExecuteResult {
17551783
guard case let .unvar(variable) = token else {
17561784
return .next

app/src/Outlander/Scripting/ScriptContext.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ class ScriptContext {
1515

1616
var lines: [ScriptLine] = []
1717
var labels: [String: Label] = [:]
18-
var variables = Variables(eventKey: "")
18+
var variables: ScriptVariables
1919
var args: [String] = []
2020
var argumentVars = Variables(eventKey: "")
2121
var actionVars = Variables(eventKey: "")
@@ -56,6 +56,7 @@ class ScriptContext {
5656

5757
init(context: GameContext) {
5858
self.context = context
59+
variables = ScriptVariables(events: context.events, settings: context.applicationSettings)
5960
tokenizer = ScriptTokenizer()
6061
}
6162

app/src/Outlander/Scripting/ScriptTokenizer.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ enum ScriptTokenValue: Hashable {
105105
case save(String)
106106
case send(String)
107107
case shift
108+
case timer(String, String)
108109
case unvar(String)
109110
case variable(String, String)
110111
case waitEval(String)
@@ -273,6 +274,8 @@ extension ScriptTokenValue: CustomStringConvertible {
273274
return "send"
274275
case .shift:
275276
return "shift"
277+
case .timer:
278+
return "timer"
276279
case .unvar:
277280
return "unvar"
278281
case .variable:
@@ -397,6 +400,7 @@ class CommandMode: IScriptReaderMode {
397400
"save": SaveMode(),
398401
"send": SendMode(),
399402
"shift": ShiftMode(),
403+
"timer": TimerMode(),
400404
"unvar": UnVarMode(),
401405
"setvariable": VariableMode(),
402406
"var": VariableMode(),
@@ -574,7 +578,8 @@ class DelayMode: IScriptReaderMode {
574578

575579
class EchoMode: IScriptReaderMode {
576580
func read(_ context: ScriptTokenizerContext) -> IScriptReaderMode? {
577-
context.text.consumeSpaces()
581+
// do not consume multiple leading spaces
582+
context.text.consumeSingleSpace()
578583
let rest = String(context.text.parseToEnd())
579584
context.target.append(.echo(rest))
580585
return nil
@@ -962,6 +967,17 @@ class ShiftMode: IScriptReaderMode {
962967
}
963968
}
964969

970+
class TimerMode: IScriptReaderMode {
971+
func read(_ context: ScriptTokenizerContext) -> IScriptReaderMode? {
972+
context.text.consumeSpaces()
973+
let command = String(context.text.parseWord()).lowercased()
974+
context.text.consumeSpaces()
975+
let maybeDate = String(context.text.parseToEnd())
976+
context.target.append(.timer(command, maybeDate))
977+
return nil
978+
}
979+
}
980+
965981
class UnVarMode: IScriptReaderMode {
966982
func read(_ context: ScriptTokenizerContext) -> IScriptReaderMode? {
967983
context.text.consumeSpaces()

app/src/Outlander/Server/GameStream.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,11 @@ extension StringView where SubSequence == Self, Element: Equatable {
380380
consume(while: { Self.isSpace($0) })
381381
}
382382

383+
mutating func consumeSingleSpace() {
384+
guard let f = first, Self.isSpace(f) else { return }
385+
removeFirst()
386+
}
387+
383388
mutating func parseToEnd() -> [Element] {
384389
parseMany(while: { _ in true })
385390
}
@@ -652,7 +657,12 @@ struct TextTag {
652657
}
653658

654659
static func lines(tags: [TextTag]) -> [String] {
655-
let combined = tags.map { $0.text }.joined(separator: "").components(separatedBy: "\n").filter { !$0.isEmpty }
660+
let combined = tags
661+
.map { $0.text }
662+
.joined(separator: "")
663+
.components(separatedBy: "\n")
664+
.map { $0.trimLeadingWhitespace() }
665+
.filter { !$0.isEmpty }
656666
return combined
657667
}
658668
}
@@ -844,7 +854,7 @@ class GameStream {
844854
}
845855

846856
public func sendToHandlers(text: String) {
847-
let lines = text.components(separatedBy: "\n").filter { !$0.isEmpty }
857+
let lines = text.components(separatedBy: "\n").map { $0.trimLeadingWhitespace() }.filter { !$0.isEmpty }
848858

849859
for line in lines {
850860
for handler in handlers {

0 commit comments

Comments
 (0)