From 5e971359701d5df8269b44dc94ba2e0f10821327 Mon Sep 17 00:00:00 2001
From: Wizzard <rich@bandaholics.cash>
Date: Mon, 10 Mar 2025 12:33:09 -0400
Subject: [PATCH] Player names!

---
 src/comms.rs           |   9 ++--
 src/dma/context/mod.rs |  22 +++++++-
 src/dma/mod.rs         |   6 ++-
 webradar/script.js     | 112 ++++++++++++++++++++++++++++++-----------
 4 files changed, 112 insertions(+), 37 deletions(-)

diff --git a/src/comms.rs b/src/comms.rs
index 252cd23..8e95bed 100755
--- a/src/comms.rs
+++ b/src/comms.rs
@@ -16,12 +16,15 @@ pub struct PlayerData {
     has_awp: bool,
 
     #[serde(rename = "isScoped")]
-    is_scoped: bool
+    is_scoped: bool,
+
+    #[serde(rename = "playerName")]
+    player_name: String
 }
 
 impl PlayerData {
-    pub fn new(pos: Vec3, yaw: f32, player_type: PlayerType, has_bomb: bool, has_awp: bool, is_scoped: bool) -> PlayerData {
-        PlayerData { pos, yaw, player_type, has_bomb, has_awp, is_scoped }
+    pub fn new(pos: Vec3, yaw: f32, player_type: PlayerType, has_bomb: bool, has_awp: bool, is_scoped: bool, player_name: String) -> PlayerData {
+        PlayerData { pos, yaw, player_type, has_bomb, has_awp, is_scoped, player_name }
     }
 }
 
diff --git a/src/dma/context/mod.rs b/src/dma/context/mod.rs
index 0cbfabc..c40a3b3 100755
--- a/src/dma/context/mod.rs
+++ b/src/dma/context/mod.rs
@@ -97,6 +97,7 @@ impl DmaCtx {
         let mut team = 0i32;
         let mut clipping_weapon = 0u64;
         let mut is_scoped = 0u8;
+        let mut player_name_ptr = 0u64;
 
         {
             let mut batcher = MemoryViewBatcher::new(&mut self.process);
@@ -106,8 +107,23 @@ impl DmaCtx {
             batcher.read_into(controller + cs2dumper::client::C_BaseEntity::m_iTeamNum, &mut team);
             batcher.read_into(pawn + cs2dumper::client::C_CSPlayerPawnBase::m_pClippingWeapon, &mut clipping_weapon);
             batcher.read_into(pawn + cs2dumper::client::C_CSPlayerPawn::m_bIsScoped, &mut is_scoped);
+            batcher.read_into(controller + cs2dumper::client::CCSPlayerController::m_sSanitizedPlayerName, &mut player_name_ptr);
         }
-    
+
+        let player_name = if player_name_ptr != 0 {
+            let mut name_buffer = [0u8; 32];
+            self.process.read_raw_into(player_name_ptr.into(), &mut name_buffer)?;
+
+            let name_len = name_buffer.iter().position(|&c| c == 0).unwrap_or(name_buffer.len());
+            String::from_utf8_lossy(&name_buffer[..name_len]).to_string()
+        } else {
+            match team {
+                2 => format!("T Player"),
+                3 => format!("CT Player"),
+                _ => format!("Unknown Player"),
+            }
+        };
+
         let team = TeamID::from_i32(team);
 
         let has_awp = {
@@ -126,7 +142,8 @@ impl DmaCtx {
             team,
             health,
             has_awp,
-            is_scoped: is_scoped != 0
+            is_scoped: is_scoped != 0,
+            player_name,
         })
     }
 
@@ -252,4 +269,5 @@ pub struct BatchedPlayerData {
     pub health: u32,
     pub has_awp: bool,
     pub is_scoped: bool,
+    pub player_name: String,
 }
\ No newline at end of file
diff --git a/src/dma/mod.rs b/src/dma/mod.rs
index b63495e..4ce8ed9 100755
--- a/src/dma/mod.rs
+++ b/src/dma/mod.rs
@@ -165,7 +165,8 @@ pub async fn run(radar_data: ArcRwlockRadarData, connector: Connector, pcileech_
                             PlayerType::Local,
                             has_bomb,
                             local_data.has_awp,
-                            local_data.is_scoped
+                            local_data.is_scoped,
+                            local_data.player_name
                         )
                     )
                 );
@@ -202,7 +203,8 @@ pub async fn run(radar_data: ArcRwlockRadarData, connector: Connector, pcileech_
                             player_type,
                             has_bomb,
                             player_data.has_awp,
-                            player_data.is_scoped
+                            player_data.is_scoped,
+                            player_data.player_name
                         )
                     )
                 );
diff --git a/webradar/script.js b/webradar/script.js
index e8b9584..b8d6fcf 100644
--- a/webradar/script.js
+++ b/webradar/script.js
@@ -40,7 +40,7 @@ if (location.protocol == 'https:') {
 
 // Util functions
 const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
-const degreesToRadians = (degrees) => degrees * (Math.PI/180);
+const degreesToRadians = (degrees) => degrees * (Math.PI / 180);
 function makeBoundingRect(x1, y1, x2, y2, aspectRatio) {
     const topLeftX = x1;
     const topLeftY = y1;
@@ -93,7 +93,7 @@ function boundingCoordinates(coordinates, boundingRect) {
     const newX = (coordinates.x - boundingRect.x) / xScale;
     const newY = (coordinates.y - boundingRect.y) / yScale;
 
-    return {x: newX, y: newY};
+    return { x: newX, y: newY };
 }
 
 function boundingScale(value, boundingRect) {
@@ -108,7 +108,48 @@ function mapCoordinates(coordinates) {
     offset_x /= map.scale;
     offset_y /= -map.scale;
 
-    return {x: offset_x, y: offset_y}
+    return { x: offset_x, y: offset_y }
+}
+
+function drawPlayerName(pos, playerName, playerType, hasAwp, hasBomb) {
+    if (!map) return;
+
+    if (zoomSet) {
+        pos = boundingCoordinates(mapCoordinates(pos), boundingRect);
+        textSize = boundingScale(12, boundingRect);
+    } else {
+        pos = mapCoordinates(pos);
+        textSize = 12;
+    }
+
+    const textY = pos.y + 20;
+
+    let displayName = playerName;
+    if (playerType === "Local") {
+        displayName = "YOU";
+        ctx.fillStyle = localColor;
+    } else if (playerType === "Team") {
+        ctx.fillStyle = teamColor;
+    } else if (playerType === "Enemy") {
+        ctx.fillStyle = enemyColor;
+    }
+
+    if (hasAwp) {
+        displayName += " [AWP]";
+    }
+    if (hasBomb) {
+        displayName += " [C4]";
+    }
+
+    ctx.font = `${textSize}px Arial`;
+    ctx.textAlign = "center";
+    ctx.textBaseline = "top";
+
+    ctx.lineWidth = 2;
+    ctx.strokeStyle = "black";
+    ctx.strokeText(displayName, pos.x, textY);
+
+    ctx.fillText(displayName, pos.x, textY);
 }
 
 function render() {
@@ -133,15 +174,14 @@ function render() {
                     } else {
                         mapCords = mapCoordinates(data.Player.pos)
                     }
-    
+
                     minX = Math.min(minX, mapCords.x);
                     minY = Math.min(minY, mapCords.y);
                     maxX = Math.max(maxX, mapCords.x);
                     maxY = Math.max(maxY, mapCords.y);
                 });
 
-
-                boundingRect = makeBoundingRect(minX-safetyBound, minY-safetyBound, maxX+safetyBound, maxY+safetyBound, image.width/image.height)
+                boundingRect = makeBoundingRect(minX - safetyBound, minY - safetyBound, maxX + safetyBound, maxY + safetyBound, image.width / image.height)
 
                 zoomSet = true
             } else if (zoomSet) {
@@ -169,14 +209,18 @@ function render() {
 
                         drawEntity(
                             data.Player.pos,
-                            fillStyle, 
+                            fillStyle,
                             data.Player.isDormant,
                             data.Player.hasBomb,
                             data.Player.yaw,
                             data.Player.hasAwp,
                             data.Player.playerType,
                             data.Player.isScoped
-                        )
+                        );
+
+                        if (drawStats && data.Player.playerName) {
+                            drawPlayerName(data.Player.pos, data.Player.playerName, data.Player.playerType);
+                        }
                     }
                 });
             }
@@ -184,13 +228,13 @@ function render() {
             if (radarData != null) {
                 if (radarData.bombPlanted && !radarData.bombExploded && radarData.bombDefuseTimeleft >= 0) {
 
-                    let maxWidth = 1024-128-128;
+                    let maxWidth = 1024 - 128 - 128;
                     let timeleft = radarData.bombDefuseTimeleft;
-    
+
                     // Base bar
                     ctx.fillStyle = "black"
                     ctx.fillRect(128, 16, maxWidth, 16)
-                    
+
                     // Bomb timer
                     if (radarData.bombBeingDefused) {
                         if (radarData.bombCanDefuse) {
@@ -202,14 +246,14 @@ function render() {
                         ctx.fillStyle = bombColor
                     }
 
-                    ctx.fillRect(130, 18, (maxWidth-2) * (timeleft / 40), 12)
+                    ctx.fillRect(130, 18, (maxWidth - 2) * (timeleft / 40), 12)
 
                     ctx.font = "24px Arial";
                     ctx.textAlign = "center"
                     ctx.textBaseline = "middle"
                     ctx.fillStyle = textColor
-                    ctx.fillText(`${timeleft.toFixed(1)}s`, 1024/2, 28+24); 
-                    
+                    ctx.fillText(`${timeleft.toFixed(1)}s`, 1024 / 2, 28 + 24);
+
                     // Defuse time lines
                     ctx.strokeStyle = "black"
                     ctx.lineWidth = 2
@@ -222,8 +266,8 @@ function render() {
 
                     // Normal defuse
                     ctx.beginPath()
-                    ctx.moveTo(130 + (maxWidth-2) * (10 / 40), 16)
-                    ctx.lineTo(130 + (maxWidth-2) * (10 / 40), 32)
+                    ctx.moveTo(130 + (maxWidth - 2) * (10 / 40), 16)
+                    ctx.lineTo(130 + (maxWidth - 2) * (10 / 40), 32)
                     ctx.stroke()
 
                     // Defuse stamp line
@@ -231,8 +275,8 @@ function render() {
                         console.log(radarData.bombDefuseEnd)
                         ctx.strokeStyle = "green"
                         ctx.beginPath()
-                        ctx.moveTo(130 + (maxWidth-2) * (radarData.bombDefuseEnd / 40), 16)
-                        ctx.lineTo(130 + (maxWidth-2) * (radarData.bombDefuseEnd / 40), 32)
+                        ctx.moveTo(130 + (maxWidth - 2) * (radarData.bombDefuseEnd / 40), 16)
+                        ctx.lineTo(130 + (maxWidth - 2) * (radarData.bombDefuseEnd / 40), 32)
                         ctx.stroke()
                     }
                 }
@@ -244,12 +288,12 @@ function render() {
                 ctx.textAlign = "center"
                 ctx.textBaseline = "middle"
                 ctx.fillStyle = textColor
-                ctx.fillText("Not on a server", 1024/2, 1024/2); 
+                ctx.fillText("Not on a server", 1024 / 2, 1024 / 2);
             } else {
                 ctx.font = "100px Arial";
                 ctx.textAlign = "center"
                 ctx.fillStyle = textColor
-                ctx.fillText("Disconnected", 1024/2, 1024/2); 
+                ctx.fillText("Disconnected", 1024 / 2, 1024 / 2);
             }
         }
 
@@ -310,7 +354,7 @@ function drawBomb(pos, planted) {
     }
 }
 
-function drawEntity(pos, fillStyle, dormant, hasBomb, yaw, hasAwp, playerType, isScoped) {
+function drawEntity(pos, fillStyle, dormant, hasBomb, yaw, hasAwp, playerType, isScoped, playerName) {
     if (map == null)
         return
 
@@ -332,7 +376,7 @@ function drawEntity(pos, fillStyle, dormant, hasBomb, yaw, hasAwp, playerType, i
         ctx.font = "20px Arial";
         ctx.textAlign = "center"
         ctx.fillStyle = fillStyle
-        ctx.fillText("?", pos.x, pos.y); 
+        ctx.fillText("?", pos.x, pos.y);
     } else {
 
         if (hasAwp) {
@@ -386,9 +430,9 @@ function drawEntity(pos, fillStyle, dormant, hasBomb, yaw, hasAwp, playerType, i
         const arrowCornerY2 = pos.y - distance * Math.sin((yaw + arrowWidth) * (Math.PI / 180))
 
 
-        const cicleYaw = 90-yaw
-        const startAngle = degreesToRadians(cicleYaw-arrowWidth)-Math.PI/2
-        const endAngle = degreesToRadians(cicleYaw+arrowWidth)-Math.PI/2
+        const cicleYaw = 90 - yaw
+        const startAngle = degreesToRadians(cicleYaw - arrowWidth) - Math.PI / 2
+        const endAngle = degreesToRadians(cicleYaw + arrowWidth) - Math.PI / 2
 
         // Draw arrow
 
@@ -462,15 +506,23 @@ function connect() {
             console.log("[radarflow] Connection established")
             websocket.send("requestInfo");
         };
-        
+
         socket.onmessage = (event) => {
             if (event.data == "error") {
                 console.log("[radarflow] Server had an unknown error")
             } else {
                 let data = JSON.parse(event.data);
-                radarData = data; 
+                radarData = data;
                 freq = data.freq;
 
+                if (data.entityData) {
+                    data.entityData.forEach(entity => {
+                        if (entity.Player) {
+                            console.log(`Player: ${entity.Player.playerType}, Name: ${entity.Player.playerName || 'undefined'}`);
+                        }
+                    });
+                }
+
                 if (data.ingame == false) {
                     mapName = null
                     entityData = null
@@ -491,7 +543,7 @@ function connect() {
                 requestAnimationFrame(render);
             }
         };
-        
+
         socket.onclose = (event) => {
             if (event.wasClean) {
                 console.log("[radarflow] connection closed");
@@ -503,11 +555,11 @@ function connect() {
             websocket = null
             unloadMap()
 
-            setTimeout(function() {
+            setTimeout(function () {
                 connect();
             }, 1000);
         };
-    
+
         socket.onerror = (error) => {
             console.log(`[radarflow] websocket error: ${error}`);
         };