Skip to content

Commit eb3c70f

Browse files
committed
Add an optional buffering mechanism for bad network connections
1 parent cdafa57 commit eb3c70f

File tree

4 files changed

+95
-29
lines changed

4 files changed

+95
-29
lines changed

config.txt

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ Auto-Connect : true
3131
Auto-Connect Delay : 1000
3232
Default Server : localhost:3200
3333
Drawing Port : 32769
34+
Monitor Step : 0.04
35+
Network Buffer : false
3436
# To store more servers, just insert more "Server" lines
3537
Server : localhost:3200
3638

src/main/java/rv/comm/rcssserver/ServerComm.java

+66-29
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,9 @@
2424
import java.io.PrintWriter;
2525
import java.net.Socket;
2626
import java.text.ParseException;
27-
import java.util.Arrays;
28-
import java.util.Calendar;
29-
import java.util.List;
30-
import java.util.Locale;
27+
import java.util.*;
3128
import java.util.concurrent.CopyOnWriteArrayList;
29+
import java.util.concurrent.LinkedBlockingQueue;
3230
import javax.swing.Timer;
3331
import jsgl.math.vector.Vec3f;
3432
import org.apache.logging.log4j.LogManager;
@@ -69,40 +67,79 @@ public MessageReceiver(String host, int port)
6967
@Override
7068
public void run()
7169
{
72-
try {
73-
socket = new Socket(host, port);
74-
out = new PrintWriter(socket.getOutputStream(), true);
75-
in = new DataInputStream(socket.getInputStream());
76-
77-
setConnected(true);
78-
if (recordLogs)
79-
setupNewLogfile();
80-
81-
String message;
82-
do {
83-
message = readMessage();
84-
if (message != null) {
85-
try {
86-
parser.parse(message);
87-
if (logfileOutput != null)
88-
writeToLogfile(message);
89-
} catch (ParseException e) {
90-
LOGGER.error("Unable to parse server message", e);
91-
}
70+
// Receive messages in a separate thread and put them in a queue
71+
LinkedBlockingQueue<Optional<String>> messages = new LinkedBlockingQueue<>(3000);
72+
new Thread(() -> {
73+
try {
74+
socket = new Socket(host, port);
75+
out = new PrintWriter(socket.getOutputStream(), true);
76+
in = new DataInputStream(socket.getInputStream());
77+
setConnected(true);
78+
String message;
79+
do {
80+
message = readMessage();
81+
messages.put(Optional.ofNullable(message));
82+
} while (message != null);
83+
} catch (IOException | InterruptedException e) {
84+
// This is fine, just leave.
85+
// The surrounding thread will quit when receiving an
86+
// empty optional.
87+
try {
88+
messages.put(Optional.empty());
89+
} catch (InterruptedException ignored) {
9290
}
93-
} while (message != null);
91+
}
9492

9593
// If the thread gets to this point the server has stopped
9694
// sending messages by closing the connection
9795
// DebugInfo.println(getClass(), "rcssserver3d closed TCP connection");
9896
disconnect();
9997
if (autoConnectTimer != null)
10098
autoConnectTimer.start();
101-
} catch (IOException e) {
102-
disconnect();
103-
if (autoConnectTimer != null)
104-
autoConnectTimer.start();
99+
}).start();
100+
101+
if (recordLogs) {
102+
setupNewLogfile();
105103
}
104+
105+
Optional<String> message = Optional.empty();
106+
long lastUpdateTimestamp = 0;
107+
long monitorStepMillis = Math.round(Networking.INSTANCE.getMonitorStep() * 1000.0);
108+
do {
109+
// Retrieve a message from the queue
110+
try {
111+
message = messages.take();
112+
} catch (InterruptedException e) {
113+
continue;
114+
}
115+
116+
if (Networking.INSTANCE.getUseBuffer()) {
117+
// Wait until the time for one monitor frame elapsed.
118+
// This ensures that two messages are not applied immediately after each
119+
// other. That may be the case with a bad network connection.
120+
long elapsed = System.currentTimeMillis() - lastUpdateTimestamp;
121+
long timeLeft = monitorStepMillis - elapsed;
122+
if (timeLeft > 0) {
123+
try {
124+
Thread.sleep(timeLeft);
125+
} catch (InterruptedException e) {
126+
e.printStackTrace();
127+
}
128+
}
129+
lastUpdateTimestamp = System.currentTimeMillis();
130+
}
131+
132+
// Process message
133+
message.ifPresent(msg -> {
134+
try {
135+
parser.parse(msg);
136+
if (logfileOutput != null)
137+
writeToLogfile(msg);
138+
} catch (ParseException e) {
139+
LOGGER.error("Unable to parse server message", e);
140+
}
141+
});
142+
} while (message.isPresent());
106143
}
107144

108145
private String readMessage() throws IOException

src/main/kotlin/org/magmaoffenburg/roboviz/configuration/Config.kt

+8
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ class Config(args: Array<String>) {
7777

7878
var currentHost = defaultServerHost
7979
var currentPort = defaultServerPort
80+
81+
var monitorStep = 0.04
82+
var useBuffer = false
8083
}
8184

8285
object OverlayVisibility {
@@ -156,6 +159,8 @@ class Config(args: Array<String>) {
156159
parser.getValue("Auto-Connect")?.let { Networking.autoConnect = it.toBoolean() }
157160
parser.getValue("Auto-Connect Delay")?.let { Networking.autoConnectDelay = it.toInt() }
158161
parser.getValue("Drawing Port")?.let { Networking.listenPort = it.toInt() }
162+
parser.getValue("Monitor Step")?.let { Networking.monitorStep = it.toDouble() }
163+
parser.getValue("Network Buffer")?.let { Networking.useBuffer = it.toBoolean() }
159164

160165
parser.getValuePairList("Server").forEach {
161166
Networking.servers.add(Pair(it.first, it.second.toInt()))
@@ -231,6 +236,9 @@ class Config(args: Array<String>) {
231236
parser.setValuePairList("Server", Networking.servers.map { Pair(it.first, it.second.toString()) })
232237
parser.setValue("Default Server", "${Networking.defaultServerHost}:${Networking.defaultServerPort}")
233238

239+
parser.setValue("Monitor Step", Networking.monitorStep.toString())
240+
parser.setValue("Network Buffer", Networking.useBuffer.toString())
241+
234242
// OverlayVisibility
235243
parser.setValue("Server Speed", OverlayVisibility.serverSpeed.toString())
236244
parser.setValue("Foul Overlay", OverlayVisibility.foulOverlay.toString())

src/main/kotlin/org/magmaoffenburg/roboviz/gui/windows/config/ServerPanel.kt

+19
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ class ServerPanel: JPanel() {
2222
private val defaultServerPortSpinner = JSpinner(SpinnerNumberModel(Networking.defaultServerPort, 0, Int.MAX_VALUE, 1))
2323
private val drawingPortLabel = JLabel("Drawing Port:")
2424
private val drawingPortSpinner = JSpinner(SpinnerNumberModel(Networking.listenPort, 0, Int.MAX_VALUE, 1))
25+
private val monitorStepLabel = JLabel("Monitor step duration:")
26+
private val monitorStepSpinner = JSpinner(SpinnerNumberModel(Networking.monitorStep, 0.0, Double.MAX_VALUE, 0.01))
27+
private val useBufferCB = JCheckBox("Use Buffer", Networking.useBuffer)
2528
private val serverListButton = JButton("Open Server List")
2629

2730
init {
@@ -62,6 +65,11 @@ class ServerPanel: JPanel() {
6265
.addComponent(drawingPortLabel, 0, 125, 125)
6366
.addComponent(drawingPortSpinner, 0, 120, 120)
6467
)
68+
.addGroup(layout.createSequentialGroup()
69+
.addComponent(monitorStepLabel, 0, 125, 125)
70+
.addComponent(monitorStepSpinner, 0, 120, 120)
71+
)
72+
.addComponent(useBufferCB, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE.toInt())
6573
.addComponent(serverListButton, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE.toInt())
6674
)
6775

@@ -87,6 +95,11 @@ class ServerPanel: JPanel() {
8795
.addComponent(drawingPortLabel)
8896
.addComponent(drawingPortSpinner)
8997
)
98+
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
99+
.addComponent(monitorStepLabel)
100+
.addComponent(monitorStepSpinner)
101+
)
102+
.addComponent(useBufferCB)
90103
.addComponent(serverListButton)
91104
)
92105
}
@@ -124,6 +137,12 @@ class ServerPanel: JPanel() {
124137
serverListButton.addActionListener {
125138
ServerListDialog.showDialog()
126139
}
140+
monitorStepSpinner.addChangeListener {
141+
Networking.monitorStep = monitorStepSpinner.value as Double
142+
}
143+
useBufferCB.addActionListener {
144+
Networking.useBuffer = useBufferCB.isSelected
145+
}
127146
}
128147

129148
}

0 commit comments

Comments
 (0)