diff --git a/client/index.js b/client/index.js
index 39d0fe0..a2e04e7 100644
--- a/client/index.js
+++ b/client/index.js
@@ -7,12 +7,14 @@ import {EntityList} from '@avocado/entity';
import {ActionRegistry} from '@avocado/input';
import {Stage} from '@avocado/graphics';
import {Vector} from '@avocado/math';
+import {SocketIoParser} from '@avocado/packet';
import {Synchronizer, Unpacker} from '@avocado/state';
import {Room, RoomView} from '@avocado/topdown';
import {World} from '@avocado/physics/matter/world';
import {clearAnimation, setAnimation, Ticker} from '@avocado/timing';
// 1st party.
import {InputPacket, KeysPacket, StatePacket} from '../common/packet';
+import {augmentParserWithThroughput} from '../common/parser-throughput';
import {WorldTime} from '../common/world-time';
import Ui from './ui';
// DOM.
@@ -38,11 +40,6 @@ stage.element.addEventListener('focus', () => {
stage.addToDom(appNode);
// World time ticker.
const worldTimeTicker = new Ticker(1 / 10);
-// UI.
-const UiComponent = ;
-ReactDOM.render(UiComponent, stage.ui);
// Handle "own" entity.
let selfEntity;
function hasSelfEntity() {
@@ -156,7 +153,22 @@ stage.on('pointerUp', (event) => {
});
let actionState = actionRegistry.state;
// Create the socket connection.
-const socket = createClient(window.location.href);
+const AugmentedParser = augmentParserWithThroughput(SocketIoParser);
+const socket = createClient(window.location.href, {
+ parser: AugmentedParser,
+});
+// Throughput ticker.
+const throughputSampleTime = 0.5;
+const throughputTicker = new Ticker(throughputSampleTime);
+const {Decoder, Encoder} = AugmentedParser;
+throughputTicker.on('tick', () => {
+ throughputTicker.emit('updateThroughput', Vector.scale([
+ Decoder.throughput,
+ Encoder.throughput,
+ ], 1 / throughputSampleTime));
+ Decoder.throughput = 0;
+ Encoder.throughput = 0;
+});
// Input messages.
const messageHandle = setInterval(() => {
// Mouse/touch movement.
@@ -199,6 +211,8 @@ const predictionHandle = setInterval(() => {
renderTicker.tick(elapsed);
// World time ticker.
worldTimeTicker.tick(elapsed);
+ // Throughput ticker.
+ throughputTicker.tick(elapsed);
// Apply environmental lighting.
let intensity = 0;
if (worldTime.hour >= 21 || worldTime.hour < 4) {
@@ -256,3 +270,9 @@ function render() {
dirty = false;
}
const renderHandle = setAnimation(render);
+// UI.
+const UiComponent = ;
+ReactDOM.render(UiComponent, stage.ui);
diff --git a/client/ui/index.js b/client/ui/index.js
index 47200ff..2573978 100644
--- a/client/ui/index.js
+++ b/client/ui/index.js
@@ -2,10 +2,14 @@
import React from 'react';
import {hot} from 'react-hot-loader/root';
// 1st party.
+import Throughput from './throughput';
import WorldTime from './world-time';
-const Ui = ({worldTimeTicker}) => {
- return ;
+const Ui = ({throughputTicker, worldTimeTicker}) => {
+ return
+
+
+ ;
};
export default hot(Ui);
diff --git a/client/ui/throughput.js b/client/ui/throughput.js
new file mode 100644
index 0000000..63351ec
--- /dev/null
+++ b/client/ui/throughput.js
@@ -0,0 +1,59 @@
+// 3rd party.
+import React, {useEffect, useState} from 'react';
+// 2nd party.
+import {compose} from '@avocado/core';
+import contempo from 'contempo';
+// 1st party.
+import {WorldTime} from '../../common/world-time';
+
+const decorate = compose(
+ contempo(`
+.throughput {
+ background-color: rgba(0, 0, 0, .3);
+ color: white;
+ font-size: 0.6em;
+ position: absolute;
+ top: 0.5em;
+ left: 0.5em;
+ line-height: 1em;
+ width: 11em;
+ padding: 0.5em;
+ font-family: monospace;
+}
+.throughput .input:before {
+ content: 'kbps \\a0in: ';
+}
+.throughput .output:before {
+ content: 'kbps out: ';
+}
+.throughput p {
+ margin: 0.25em;
+}
+ `),
+);
+
+const formatKbps = (bytes) => {
+ const bps = bytes * 8;
+ const kbps = bps / 1000;
+ return Math.floor(kbps * 100) / 100;
+}
+
+const ThroughputComponent = ({ticker}) => {
+ const [throughput, setThroughput] = useState([0, 0]);
+ useEffect(() => {
+ const updater = (throughput) => {
+ setThroughput(throughput);
+ };
+ ticker.on('updateThroughput', updater);
+ return () => {
+ ticker.off('updateThroughput', updater);
+ };
+ }, []);
+ return
+
{formatKbps(throughput[0])}
+
{formatKbps(throughput[1])}
+
+
;
+}
+
+export default decorate(ThroughputComponent);
diff --git a/common/parser-throughput.js b/common/parser-throughput.js
new file mode 100644
index 0000000..2d2e4c9
--- /dev/null
+++ b/common/parser-throughput.js
@@ -0,0 +1,20 @@
+
+export function augmentParserWithThroughput(Parser) {
+ const {Decoder, Encoder} = Parser;
+ // Decoder.
+ Decoder.throughput = 0;
+ const {parseBinary} = Decoder.prototype;
+ Decoder.prototype.parseBinary = function (obj) {
+ Decoder.throughput += obj.length || obj.byteLength;
+ return parseBinary.call(this, obj);
+ };
+ // Encoder.
+ Encoder.throughput = 0;
+ const {pack} = Encoder.prototype;
+ Encoder.prototype.pack = function (packet) {
+ const buffer = pack.call(this, packet);
+ Encoder.throughput += buffer.length || buffer.byteLength;
+ return buffer;
+ };
+ return Parser;
+}
\ No newline at end of file