@@ -1612,3 +1612,181 @@ describe('MC Integration - entity tag methods (@s.tag/untag/has_tag)', () => {
16121612 console . log ( ` has_after_untag=${ hasAfterUntag } (expect 0) ✓` )
16131613 } , 20000 )
16141614} )
1615+
1616+ // ===========================================================================
1617+ // fft.mcrs — MC integration test for dft_real array param dynamic indexing
1618+ // ===========================================================================
1619+ describe ( 'MC Integration - fft.mcrs dft_real' , ( ) => {
1620+ const NS = 'fft_mc_test'
1621+
1622+ beforeAll ( async ( ) => {
1623+ if ( ! serverOnline ) return
1624+
1625+ const MATH_SRC = fs . readFileSync ( path . join ( __dirname , '../stdlib/math.mcrs' ) , 'utf-8' )
1626+ const FFT_SRC = fs . readFileSync ( path . join ( __dirname , '../stdlib/fft.mcrs' ) , 'utf-8' )
1627+
1628+ const fftSrc = `
1629+ namespace fft_mc_test
1630+
1631+ @load fn __load() {
1632+ raw("scoreboard objectives add fft_obj dummy");
1633+ _math_init()
1634+ }
1635+
1636+ @keep fn test_dft_re0(): int {
1637+ let input: int[] = [10000, 10000, 10000, 10000]
1638+ let re: int[] = [0, 0, 0, 0]
1639+ let im: int[] = [0, 0, 0, 0]
1640+ dft_real(input, 4, re, im)
1641+ let v: int = re[0]
1642+ scoreboard_set("#dft_re0", "fft_obj", v)
1643+ return v
1644+ }
1645+
1646+ @keep fn test_dft_mag1(): int {
1647+ let input: int[] = [10000, 0, -10000, 0]
1648+ let re: int[] = [0, 0, 0, 0]
1649+ let im: int[] = [0, 0, 0, 0]
1650+ dft_real(input, 4, re, im)
1651+ let mag: int = dft_magnitude(re, im, 1)
1652+ scoreboard_set("#dft_mag1", "fft_obj", mag)
1653+ return mag
1654+ }
1655+ `
1656+
1657+ // Use compile directly with librarySources (writeFixture doesn't support libs)
1658+ fs . mkdirSync ( DATAPACK_DIR , { recursive : true } )
1659+ if ( ! fs . existsSync ( path . join ( DATAPACK_DIR , 'pack.mcmeta' ) ) ) {
1660+ fs . writeFileSync ( path . join ( DATAPACK_DIR , 'pack.mcmeta' ) , JSON . stringify ( {
1661+ pack : { pack_format : 48 , description : 'RedScript integration tests' }
1662+ } ) )
1663+ }
1664+ const fftResult = compile ( fftSrc , { namespace : NS , librarySources : [ MATH_SRC , FFT_SRC ] } )
1665+ for ( const file of fftResult . files ) {
1666+ if ( file . path === 'pack.mcmeta' ) continue
1667+ const filePath = path . join ( DATAPACK_DIR , file . path )
1668+ fs . mkdirSync ( path . dirname ( filePath ) , { recursive : true } )
1669+ if ( file . path . includes ( 'data/minecraft/tags/' ) && fs . existsSync ( filePath ) ) {
1670+ const existing = JSON . parse ( fs . readFileSync ( filePath , 'utf-8' ) )
1671+ const incoming = JSON . parse ( file . content )
1672+ const merged = { values : [ ...new Set ( [ ...( existing . values ?? [ ] ) , ...( incoming . values ?? [ ] ) ] ) ] }
1673+ fs . writeFileSync ( filePath , JSON . stringify ( merged , null , 2 ) )
1674+ } else {
1675+ fs . writeFileSync ( filePath , file . content )
1676+ }
1677+ }
1678+
1679+ await mc . reload ( )
1680+ await mc . ticks ( 10 )
1681+ await mc . command ( `/function ${ NS } :__load` ) . catch ( ( ) => { } )
1682+ await mc . ticks ( 5 )
1683+ } )
1684+
1685+ test ( 'dft_real DC input: re[0] == 40000' , async ( ) => {
1686+ if ( ! serverOnline ) return
1687+ await mc . command ( `/function ${ NS } :test_dft_re0` )
1688+ await mc . ticks ( 3 )
1689+ const val = await mc . scoreboard ( '#dft_re0' , 'fft_obj' )
1690+ expect ( val ) . toBe ( 40000 )
1691+ } , 20000 )
1692+
1693+ test ( 'dft_real quarter-wave: X[1] magnitude ≈ 20000' , async ( ) => {
1694+ if ( ! serverOnline ) return
1695+ await mc . command ( `/function ${ NS } :test_dft_mag1` )
1696+ await mc . ticks ( 3 )
1697+ const val = await mc . scoreboard ( '#dft_mag1' , 'fft_obj' )
1698+ expect ( val ) . toBeGreaterThanOrEqual ( 19500 )
1699+ expect ( val ) . toBeLessThanOrEqual ( 20500 )
1700+ } , 20000 )
1701+ } )
1702+
1703+ // ===========================================================================
1704+ // events.mcrs — MC integration test for @on(EventType) event handler system
1705+ // ===========================================================================
1706+ describe ( 'MC Integration - @on(EventType) event system' , ( ) => {
1707+ const NS = 'ev_mc_test'
1708+ let botOnline = false
1709+
1710+ beforeAll ( async ( ) => {
1711+ if ( ! serverOnline ) return
1712+
1713+ const EVENTS_SRC = fs . readFileSync ( path . join ( __dirname , '../stdlib/events.mcrs' ) , 'utf-8' )
1714+
1715+ const evSrc = `
1716+ namespace ev_mc_test
1717+
1718+ @on(PlayerJoin) fn on_join(p: Player) {
1719+ raw("scoreboard objectives add ev_obj dummy");
1720+ scoreboard_add(p, "ev_obj", 1)
1721+ }
1722+
1723+ @on(PlayerDeath) fn on_death(p: Player) {
1724+ raw("scoreboard objectives add ev_obj dummy");
1725+ scoreboard_add(p, "ev_obj", 100)
1726+ }
1727+ `
1728+
1729+ // Compile with events.mcrs as library source
1730+ fs . mkdirSync ( DATAPACK_DIR , { recursive : true } )
1731+ if ( ! fs . existsSync ( path . join ( DATAPACK_DIR , 'pack.mcmeta' ) ) ) {
1732+ fs . writeFileSync ( path . join ( DATAPACK_DIR , 'pack.mcmeta' ) , JSON . stringify ( {
1733+ pack : { pack_format : 48 , description : 'RedScript integration tests' }
1734+ } ) )
1735+ }
1736+ const evResult = compile ( evSrc , { namespace : NS , librarySources : [ EVENTS_SRC ] } )
1737+ for ( const file of evResult . files ) {
1738+ if ( file . path === 'pack.mcmeta' ) continue
1739+ const filePath = path . join ( DATAPACK_DIR , file . path )
1740+ fs . mkdirSync ( path . dirname ( filePath ) , { recursive : true } )
1741+ if ( file . path . includes ( 'data/minecraft/tags/' ) && fs . existsSync ( filePath ) ) {
1742+ const existing = JSON . parse ( fs . readFileSync ( filePath , 'utf-8' ) )
1743+ const incoming = JSON . parse ( file . content )
1744+ const merged = { values : [ ...new Set ( [ ...( existing . values ?? [ ] ) , ...( incoming . values ?? [ ] ) ] ) ] }
1745+ fs . writeFileSync ( filePath , JSON . stringify ( merged , null , 2 ) )
1746+ } else {
1747+ fs . writeFileSync ( filePath , file . content )
1748+ }
1749+ }
1750+
1751+ await mc . reload ( )
1752+ await mc . ticks ( 20 )
1753+
1754+ // Check if TestBot is online
1755+ const status = await mc . status ( )
1756+ botOnline = status . playerNames ?. includes ( 'TestBot' ) ?? false
1757+ } )
1758+
1759+ test ( 'events.mcrs dispatcher loads without error' , async ( ) => {
1760+ if ( ! serverOnline ) return
1761+ // If we got here, reload succeeded
1762+ expect ( serverOnline ) . toBe ( true )
1763+ } , 10000 )
1764+
1765+ test ( 'PlayerJoin: removing rs.joined tag fires @on(PlayerJoin) handlers' , async ( ) => {
1766+ if ( ! serverOnline || ! botOnline ) return
1767+
1768+ // Reset score
1769+ await mc . command ( `scoreboard objectives add ev_obj dummy` ) . catch ( ( ) => { } )
1770+ await mc . command ( `scoreboard players set TestBot ev_obj 0` )
1771+
1772+ // Simulate "join" by removing the rs.joined tag
1773+ await mc . command ( `tag TestBot remove rs.joined` )
1774+ await mc . ticks ( 3 ) // wait for @tick to fire
1775+
1776+ const score = await mc . scoreboard ( 'TestBot' , 'ev_obj' )
1777+ expect ( score ) . toBeGreaterThanOrEqual ( 1 )
1778+ console . log ( ` PlayerJoin handler fired: ev_obj = ${ score } (expect >= 1) ✓` )
1779+ } , 20000 )
1780+
1781+ test ( 'PlayerDeath: killing TestBot fires @on(PlayerDeath) handlers' , async ( ) => {
1782+ if ( ! serverOnline || ! botOnline ) return
1783+
1784+ await mc . command ( `scoreboard players set TestBot ev_obj 0` )
1785+ await mc . command ( `kill TestBot` )
1786+ await mc . ticks ( 5 )
1787+
1788+ const score = await mc . scoreboard ( 'TestBot' , 'ev_obj' )
1789+ expect ( score ) . toBeGreaterThanOrEqual ( 100 )
1790+ console . log ( ` PlayerDeath handler fired: ev_obj = ${ score } (expect >= 100) ✓` )
1791+ } , 20000 )
1792+ } )
0 commit comments